Francesco Petrogalli via llvm-dev
2016-Nov-30 15:10 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Dear all, I have just created a couple of differential reviews to enable the vectorisation of loops that have function calls to routines marked with “#pragma omp declare simd”. They can be (re)viewed here: * https://reviews.llvm.org/D27249 * https://reviews.llvm.org/D27250 The current implementation allows the loop vectorizer to generate vector code for source file as: #pragma omp declare simd double f(double x); void aaa(double *x, double *y, int N) { for (int i = 0; i < N; ++i) { x[i] = f(y[i]); } } by invoking clang with arguments: $> clang -fopenmp -c -O3 file.c […] Such functionality should provide a nice interface for vector libraries developers that can be used to inform the loop vectorizer of the availability of an external library with the vector implementation of the scalar functions in the loops. For this, all is needed to do is to mark with “#pragma omp declare simd” the function declaration in the header file of the library and generate the associated symbols in the object file of the library according to the name scheme of the vector ABI (see notes below). I am interested in any feedback/suggestion/review the community might have regarding this behaviour. Below you find a description of the implementation and some notes. Thanks, Francesco ----------- The functionality is implemented as follow: 1. Clang CodeGen generates a set of global external variables for each of the function declarations marked with the OpenMP pragma. Each of such globals are named according a mangling that is generated by llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the associated vector function. (See examples in the tests of the clang patch. Each scalar function can generate multiple vector functions depending on the clauses of the declare simd directives) 2. When clang created the TLII, it processes the llvm::Module and finds out which of the globals of the module have the correct mangling and type so that they be added to the TLII as a list of vector function that can be associated to the original scalar one. 3. The LoopVectorizer looks for the available vector functions through the TLII not by scalar name and vectorisation factor but by scalar name and vector function signature, thus enabling the vectorizer to be able to distinguish a "vector vpow1(vector x, vector y)” from a “vector vpow2(vector x, scalar y)”. (The second one corresponds to a “declare simd uniform(y)” for a “scalar pow(scalar x, scalar y)” declaration). (Notice that the changes in the loop vectorizer are minimal.) Notes: 1. To enable SIMD only for OpenMP, leaving all the multithread/target behaviour behind, we should enable this also with a new option: -fopenmp-simd 2. The AArch64 vector ABI in the code is essentially the same as for the Intel one (apart from the prefix and the masking argument), and it is based on the clauses associated to “declare simd” in OpenMP 4.0. For OpenMP4.5, the parameters section of the mangled name should be updated. This update will not change the vectorizer behaviour as all the vectorizer needs to detect a vectorizable function is the original scalar name and a compatible vector function signature. Of course, any changes/updates in the ABI will have to be reflected in the symbols of the binary file of the library. 3. Whistle this is working only for function declaration, the same functionality can be used when (if) clang will implement the declare simd OpenMP pragma for function definitions. 4. I have enabled this for any loop that invokes the scalar function call, not just for those annotated with “#pragma omp for simd”. I don’t have any preference here, but at the same time I don’t see any reason why this shouldn’t be enabled by default for non annotated loops. Let me know if you disagree, I’d happily change the functionality if there are sound reasons behind that.
Tian, Xinmin via llvm-dev
2016-Nov-30 17:16 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Hi Francesco, Good to know, you are working on the support for this feature. I assume you knew the RFC below. The VectorABI mangling we proposed were approved by C++ Clang FE name mangling owner David M from Google, the ClangFE support was committed in its main trunk by Alexey. “Proposal for function vectorization and loop vectorization with function calls”, March 2, 2016. Intel Corp. http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html. Matt submitted patch to generate vector variants for function definitions, not just function declarations. You may want to take a look. Ayal's RFC will be also needed to support vectorization of function body in general. I agreed, we should have an option -fopenmp-simd to enable SIMD only, both GCC and ICC have similar options. I would suggest we shall sync-up on these work, so we don't duplicate the effort. Thanks, Xinmin -----Original Message----- From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of Francesco Petrogalli via llvm-dev Sent: Wednesday, November 30, 2016 7:11 AM To: llvm-dev at lists.llvm.org Cc: nd <nd at arm.com> Subject: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Dear all, I have just created a couple of differential reviews to enable the vectorisation of loops that have function calls to routines marked with “#pragma omp declare simd”. They can be (re)viewed here: * https://reviews.llvm.org/D27249 * https://reviews.llvm.org/D27250 The current implementation allows the loop vectorizer to generate vector code for source file as: #pragma omp declare simd double f(double x); void aaa(double *x, double *y, int N) { for (int i = 0; i < N; ++i) { x[i] = f(y[i]); } } by invoking clang with arguments: $> clang -fopenmp -c -O3 file.c […] Such functionality should provide a nice interface for vector libraries developers that can be used to inform the loop vectorizer of the availability of an external library with the vector implementation of the scalar functions in the loops. For this, all is needed to do is to mark with “#pragma omp declare simd” the function declaration in the header file of the library and generate the associated symbols in the object file of the library according to the name scheme of the vector ABI (see notes below). I am interested in any feedback/suggestion/review the community might have regarding this behaviour. Below you find a description of the implementation and some notes. Thanks, Francesco ----------- The functionality is implemented as follow: 1. Clang CodeGen generates a set of global external variables for each of the function declarations marked with the OpenMP pragma. Each of such globals are named according a mangling that is generated by llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the associated vector function. (See examples in the tests of the clang patch. Each scalar function can generate multiple vector functions depending on the clauses of the declare simd directives) 2. When clang created the TLII, it processes the llvm::Module and finds out which of the globals of the module have the correct mangling and type so that they be added to the TLII as a list of vector function that can be associated to the original scalar one. 3. The LoopVectorizer looks for the available vector functions through the TLII not by scalar name and vectorisation factor but by scalar name and vector function signature, thus enabling the vectorizer to be able to distinguish a "vector vpow1(vector x, vector y)” from a “vector vpow2(vector x, scalar y)”. (The second one corresponds to a “declare simd uniform(y)” for a “scalar pow(scalar x, scalar y)” declaration). (Notice that the changes in the loop vectorizer are minimal.) Notes: 1. To enable SIMD only for OpenMP, leaving all the multithread/target behaviour behind, we should enable this also with a new option: -fopenmp-simd 2. The AArch64 vector ABI in the code is essentially the same as for the Intel one (apart from the prefix and the masking argument), and it is based on the clauses associated to “declare simd” in OpenMP 4.0. For OpenMP4.5, the parameters section of the mangled name should be updated. This update will not change the vectorizer behaviour as all the vectorizer needs to detect a vectorizable function is the original scalar name and a compatible vector function signature. Of course, any changes/updates in the ABI will have to be reflected in the symbols of the binary file of the library. 3. Whistle this is working only for function declaration, the same functionality can be used when (if) clang will implement the declare simd OpenMP pragma for function definitions. 4. I have enabled this for any loop that invokes the scalar function call, not just for those annotated with “#pragma omp for simd”. I don’t have any preference here, but at the same time I don’t see any reason why this shouldn’t be enabled by default for non annotated loops. Let me know if you disagree, I’d happily change the functionality if there are sound reasons behind that. _______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Francesco Petrogalli via llvm-dev
2016-Dec-06 15:21 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Hi Xinmin, Thank you for your email. I have been catching up with the content of your proposal, and I have some questions/remarks below that I’d like to discuss with you - see the final section in the proposal. I have specifically added Alexey B. to the mail so we can move our conversation from phabricator to the mailing list. Before we start, I just want to mention that the initial idea of using llvm::FunctionType for vector function generation and matching has been proposed by a colleague, Paul Walker, when we first tried out supporting this on AArch64 on an internal version of llvm. I received some input also from Amara Emerson. In our case we had a slightly different problem to solve: we wanted to support in the vectorizer a rich set of vector math routines provided with an external library. We managed to do this by adding the pragma to the (scalar) function declaration of the header file provided with the library, and as shown by the patches I have submitted, by generating vector function signatures that the vectorizer can search in the TargetLibraryInfo. Here is an updated version of the proposal. Please let me know what you think, and if you have any solution we could use for the final section. # RFC for "pragma omp declare simd" Hight level components: A) Global variable generator (clang FE) B) Parameter descriptors (as new enumerations in llvm::Attribute) C) TLII methods and fields for the multimap (llvm middle-end) ## Workflow Example user input, with a declaration and definition: #pragma omp declare simd #pragma omp declare simd uniform(y) extern double pow(double x, double y); #pragma omp declare simd #pragma omp declare simd linear(x:2) float foo(float x) {....} /// code using both functions ### Step 1 The compiler FE process these definition and declaration and generates a list of globals as follows: @prefix_vector_pow1_midfix_pow_postfix = external global <4 x double>(<4 x double>, <4 x double>) @prefix_vector_pow2_midfix_pow_postfix = external global <4 x double>(<4 x double>, double) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float>) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float> #0) ... attribute #0 = {linear = 2} Notes about step 1: 1. The mapping scalar name <-> vector name is in the prefix/midfix/postfix mangled name of the global variable. 2. The examples shows only a set of possible vector function for a sizeof(<4 x double>) vector extension. If multiple vector extension live in the same target (eg. NEON 64-bit or NEON 128-bit, or SSE and AVX512) the front end takes care to generate each of the associated functions (like it is done now). 3. Vector function parameters are rendered using the same Characteristic Data Type (CDT) rule already in the compiler FE. 4. Uniform parameters are rendered with the original scalar type. 5. Linear parameters are rendered with vectors using the same CDT-generated vector length, and decorated with proper attributes. I think we could extent the llvm::Attribute enumeration adding the following: - linear : numeric, specify_the step - linear_var : numeric, specify the position of the uniform variable holding the step - linear_uval[_var]: numeric as before, but for the "uval" modifier (both constant step or variable step) - linear_val[_var]: numeric, as before, but for "val" modifier - linear_ref[_var] numeric, for "ref" modifier. For example, "attribute #0 = {linear = 2}" says that the vector of the associated parameter in the function signature has a linear step of 2. ### Step 2 The compiler FE invokes a TLII method in BackendUtils.cpp that populate a multimap in the TLII by checking the globals created in the previous step. Each global is processed, demangling the [pre/mid/post]fix name and generate a mapping in the TLII as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; }; std::multimap<std:string, VectorFnInfo> VFInfo; For the initial example, the multimap in the TLI is populated as follows: "pow" -> [(vector_pow1, <4 x double>(<4 x double>, <4 x double>)), (vector_pow2, <4 x double>(<4 x double>, double))] "foo" -> [(vector_foo1, <8 x float>(<8 x float>, <8 x float>)), (vector_foo2, <8 x float>(<8 x float>, <8 x float> #0))] Notes about step 2: Given the fact that the external globals that the FE have generated are removed _before_ the vectorizer kicks in, I am not sure if the "attribute #0" needed for one of the parameter is still present at this point. IF NOT, I think that in this case we could enrich the "VectorFnInfo" as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; std::set<unsigned, llvm:Attribute> Attrs; }; The field "Attrs" maps the position of the parameter with the correspondent llvm::Attribute present in the global variable. I have added this note for the sake of completeness. I *think* that we won't be needing this additional Attrs field: I have already shown in the llvm patch I submitted that the function type "survives" after the global gets removed, I don't see why the parameter attribute shouldn't survive too (last famous words?). ### Step 3 This step happens in the LoopVectorizer. The InnerLoopVectorizer queries the TargetLibraryInfo looking for a vectorized version of the function by scalar name and function signature with the following method: TargetLibraryInfo::isFunctionVectorizable(std::string ScalarName, FuncionType *FTy); This is done in a way similar to what my current llvm patch does: the loop vectorizer makes up the function signature it needs and look for it in the TLI. If a match is found, vectorization is possible. Right now the compiler is not aware of uniform/linear function attributes, but it still can refer to them in a target agnostic way, by using scalar signatures for the uniform ones and using llvm::Attributes for the linear ones. Notice that the vector name here is not used at all, which is good as any architecture can come up with it's own name mangling for vector functions, without breaking the ability of the vectorizer to vectorize the same code with the new name mangling. ## External libraries vs user provided code The example with "pow" and "foo" I have provided before shows a function declaration and a function definition. Although the TLII mechanism I have described seems to be valid only for the former case, I think that it is valid also for the latter. In fact, in case of a function definition, the compiler would have to generate also the body of the vector function, but that external global variable could still be used to inform the TLII of such function. The fact that the vector function needed by the vectorizer is in some module instead of in an external library doesn't seems to make all that difference at compile time to me. # Some final notes (call for ideas!) There is one level of target dependence that I still have to sort out, and for this I need input from the community and in particular from the Intel folks. I will start with this example: #pragma omp declare simd float foo(float x); In case of NEON, this would generate 2 globals, one for vectors holding 2 floats, and one for vector holding 4 floats, corresponding to NEON 64-bit and 128-bit respectively. This means that the vectorizer have a unique function it could choose from the list the TLI provides. This is not the same on Intel, for example when this code generates vector names for AVX and AVX2. The register width for these architecture extensions are the same, so all the TLI has is a mapping between scalar name and (vectro_name, function_type) who's two elements differ only in the vector_name string. This breaks the target independence of the vectorizer, as it would require it to parse the vector_name to be able to choose between the AVX or the AVX2 implementation. Now, to make this work one should have to encode the SSE/SSE2/AVX/AVX2 information in the VectorFnInfo structure. Does anybody have an idea on how best to do it? For the sake of keeping the vectorizer target independent, I would like to avoid encoding this piece of information in the VectorFnInfo struct. I have seen that in your code you are generating SSE/AVX/AVX2/AVX512 vector functions, how do you plan to choose between them in the vectorizer? I could not find how you planned to solve this problem in your proposal, or have I just missed it? Is there a way to do this in the TLII? The function type of the vector function could use the "target-feature" attribute of function definitions, but how coudl the vectorizer decide which one to use? Anyway, that's it. Your feedback will be much appreciated. Cheers, Francesco ________________________________________ From: Tian, Xinmin <xinmin.tian at intel.com> Sent: 30 November 2016 17:16:12 To: Francesco Petrogalli; llvm-dev at lists.llvm.org Cc: nd; Masten, Matt; Hal Finkel; Zaks, Ayal Subject: RE: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Francesco, Good to know, you are working on the support for this feature. I assume you knew the RFC below. The VectorABI mangling we proposed were approved by C++ Clang FE name mangling owner David M from Google, the ClangFE support was committed in its main trunk by Alexey. “Proposal for function vectorization and loop vectorization with function calls”, March 2, 2016. Intel Corp. http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html. Matt submitted patch to generate vector variants for function definitions, not just function declarations. You may want to take a look. Ayal's RFC will be also needed to support vectorization of function body in general. I agreed, we should have an option -fopenmp-simd to enable SIMD only, both GCC and ICC have similar options. I would suggest we shall sync-up on these work, so we don't duplicate the effort. Thanks, Xinmin -----Original Message----- From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of Francesco Petrogalli via llvm-dev Sent: Wednesday, November 30, 2016 7:11 AM To: llvm-dev at lists.llvm.org Cc: nd <nd at arm.com> Subject: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Dear all, I have just created a couple of differential reviews to enable the vectorisation of loops that have function calls to routines marked with “#pragma omp declare simd”. They can be (re)viewed here: * https://reviews.llvm.org/D27249 * https://reviews.llvm.org/D27250 The current implementation allows the loop vectorizer to generate vector code for source file as: #pragma omp declare simd double f(double x); void aaa(double *x, double *y, int N) { for (int i = 0; i < N; ++i) { x[i] = f(y[i]); } } by invoking clang with arguments: $> clang -fopenmp -c -O3 file.c […] Such functionality should provide a nice interface for vector libraries developers that can be used to inform the loop vectorizer of the availability of an external library with the vector implementation of the scalar functions in the loops. For this, all is needed to do is to mark with “#pragma omp declare simd” the function declaration in the header file of the library and generate the associated symbols in the object file of the library according to the name scheme of the vector ABI (see notes below). I am interested in any feedback/suggestion/review the community might have regarding this behaviour. Below you find a description of the implementation and some notes. Thanks, Francesco ----------- The functionality is implemented as follow: 1. Clang CodeGen generates a set of global external variables for each of the function declarations marked with the OpenMP pragma. Each of such globals are named according a mangling that is generated by llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the associated vector function. (See examples in the tests of the clang patch. Each scalar function can generate multiple vector functions depending on the clauses of the declare simd directives) 2. When clang created the TLII, it processes the llvm::Module and finds out which of the globals of the module have the correct mangling and type so that they be added to the TLII as a list of vector function that can be associated to the original scalar one. 3. The LoopVectorizer looks for the available vector functions through the TLII not by scalar name and vectorisation factor but by scalar name and vector function signature, thus enabling the vectorizer to be able to distinguish a "vector vpow1(vector x, vector y)” from a “vector vpow2(vector x, scalar y)”. (The second one corresponds to a “declare simd uniform(y)” for a “scalar pow(scalar x, scalar y)” declaration). (Notice that the changes in the loop vectorizer are minimal.) Notes: 1. To enable SIMD only for OpenMP, leaving all the multithread/target behaviour behind, we should enable this also with a new option: -fopenmp-simd 2. The AArch64 vector ABI in the code is essentially the same as for the Intel one (apart from the prefix and the masking argument), and it is based on the clauses associated to “declare simd” in OpenMP 4.0. For OpenMP4.5, the parameters section of the mangled name should be updated. This update will not change the vectorizer behaviour as all the vectorizer needs to detect a vectorizable function is the original scalar name and a compatible vector function signature. Of course, any changes/updates in the ABI will have to be reflected in the symbols of the binary file of the library. 3. Whistle this is working only for function declaration, the same functionality can be used when (if) clang will implement the declare simd OpenMP pragma for function definitions. 4. I have enabled this for any loop that invokes the scalar function call, not just for those annotated with “#pragma omp for simd”. I don’t have any preference here, but at the same time I don’t see any reason why this shouldn’t be enabled by default for non annotated loops. Let me know if you disagree, I’d happily change the functionality if there are sound reasons behind that. _______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Odeh, Saher via llvm-dev
2016-Dec-08 11:48 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Hi Francesco, As you stated in the RFC, when vectorizing a scalar function (e.g. when using omp declare simd), one needs to incorporate attributes to the resulting vectorized-function. These attributes describe a) the behavior of the function, e.g. mask-able or not, and b) the type of the parameters, e.g. scalar or linear or any other option. As this list is extensive, it is only logical to use an existing infrastructure of ICC and GCC vectorABI which already covers all of these options as stated in Xinmin's RFC [http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html]. Moreover, when considering other compilers such as GCC, I do see that the resulting assembly actually does incorporate this exact infrastructure. So if we wish to link different parts of the program using clang and GCC we'll need to adhere to the same name mangling/ABI. Please see the below result after compiling an omp declare simd function using GCC. Lastly, please note the two out of the three components of the implementation have already been committed or submitted, and both are adhering the name mangling proposed by Xinmin's RFC. A) committed - the FE portion by Alexey [https://reviews.llvm.org/rL264853], it generates mangled names in the manner described by Xinmin's RFC, See below B) Submitted - the callee side by Matt [https://reviews.llvm.org/D22792], it uses these mangled names. and C) caller which is covered by this patch. In order to mitigate the needed effort and possible issues when implementing, I believe it is best to follow the name mangling proposed in Xinmin's RFC. What do you think? GCC Example ---------------- Compiler version: GCC 6.1.0 Compile line: gcc -c omp.c -fopenmp -Wall -S -o - -O3 > omp.s omp.c #include <omp.h> #pragma omp declare simd int dowork(int* a, int idx) { return a[idx] * a[idx]*7; } less omp.s | grep @function .type dowork, @function .type _ZGVbN4vv_dowork, @function .type _ZGVbM4vv_dowork, @function .type _ZGVcN4vv_dowork, @function .type _ZGVcM4vv_dowork, @function .type _ZGVdN8vv_dowork, @function .type _ZGVdM8vv_dowork, @function .type _ZGVeN16vv_dowork, @function .type _ZGVeM16vv_dowork, @function Clang on FE using Alexey's patch --------------------------------------- Compile line: clang -c tst/omp_fun.c -fopenmp -mllvm -print-after-all >& out #pragma omp declare simd extern int dowork(int* a, int idx) { return a[idx]*7; } int main() { dowork(0,1); } attributes #0 = { nounwind uwtable "_ZGVbM4vv_dowork" "_ZGVbN4vv_dowork" "_ZGVcM8vv_dowork" "_ZGVcN8vv_dowork" "_ZGVdM8vv_dowork" "_ZGVdN8vv_dowork" "_ZGVeM16vv_dowork" "_ZGVeN16vv_dowork" "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } Thanks Saher -----Original Message----- From: Francesco Petrogalli [mailto:Francesco.Petrogalli at arm.com] Sent: Tuesday, December 06, 2016 17:22 To: Tian, Xinmin <xinmin.tian at intel.com>; llvm-dev at lists.llvm.org Cc: nd <nd at arm.com>; Masten, Matt <matt.masten at intel.com>; Hal Finkel <hfinkel at anl.gov>; Zaks, Ayal <ayal.zaks at intel.com>; a.bataev at hotmail.com Subject: Re: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Xinmin, Thank you for your email. I have been catching up with the content of your proposal, and I have some questions/remarks below that I'd like to discuss with you - see the final section in the proposal. I have specifically added Alexey B. to the mail so we can move our conversation from phabricator to the mailing list. Before we start, I just want to mention that the initial idea of using llvm::FunctionType for vector function generation and matching has been proposed by a colleague, Paul Walker, when we first tried out supporting this on AArch64 on an internal version of llvm. I received some input also from Amara Emerson. In our case we had a slightly different problem to solve: we wanted to support in the vectorizer a rich set of vector math routines provided with an external library. We managed to do this by adding the pragma to the (scalar) function declaration of the header file provided with the library, and as shown by the patches I have submitted, by generating vector function signatures that the vectorizer can search in the TargetLibraryInfo. Here is an updated version of the proposal. Please let me know what you think, and if you have any solution we could use for the final section. # RFC for "pragma omp declare simd" Hight level components: A) Global variable generator (clang FE) B) Parameter descriptors (as new enumerations in llvm::Attribute) C) TLII methods and fields for the multimap (llvm middle-end) ## Workflow Example user input, with a declaration and definition: #pragma omp declare simd #pragma omp declare simd uniform(y) extern double pow(double x, double y); #pragma omp declare simd #pragma omp declare simd linear(x:2) float foo(float x) {....} /// code using both functions ### Step 1 The compiler FE process these definition and declaration and generates a list of globals as follows: @prefix_vector_pow1_midfix_pow_postfix = external global <4 x double>(<4 x double>, <4 x double>) @prefix_vector_pow2_midfix_pow_postfix = external global <4 x double>(<4 x double>, double) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float>) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float> #0) ... attribute #0 = {linear = 2} Notes about step 1: 1. The mapping scalar name <-> vector name is in the prefix/midfix/postfix mangled name of the global variable. 2. The examples shows only a set of possible vector function for a sizeof(<4 x double>) vector extension. If multiple vector extension live in the same target (eg. NEON 64-bit or NEON 128-bit, or SSE and AVX512) the front end takes care to generate each of the associated functions (like it is done now). 3. Vector function parameters are rendered using the same Characteristic Data Type (CDT) rule already in the compiler FE. 4. Uniform parameters are rendered with the original scalar type. 5. Linear parameters are rendered with vectors using the same CDT-generated vector length, and decorated with proper attributes. I think we could extent the llvm::Attribute enumeration adding the following: - linear : numeric, specify_the step - linear_var : numeric, specify the position of the uniform variable holding the step - linear_uval[_var]: numeric as before, but for the "uval" modifier (both constant step or variable step) - linear_val[_var]: numeric, as before, but for "val" modifier - linear_ref[_var] numeric, for "ref" modifier. For example, "attribute #0 = {linear = 2}" says that the vector of the associated parameter in the function signature has a linear step of 2. ### Step 2 The compiler FE invokes a TLII method in BackendUtils.cpp that populate a multimap in the TLII by checking the globals created in the previous step. Each global is processed, demangling the [pre/mid/post]fix name and generate a mapping in the TLII as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; }; std::multimap<std:string, VectorFnInfo> VFInfo; For the initial example, the multimap in the TLI is populated as follows: "pow" -> [(vector_pow1, <4 x double>(<4 x double>, <4 x double>)), (vector_pow2, <4 x double>(<4 x double>, double))] "foo" -> [(vector_foo1, <8 x float>(<8 x float>, <8 x float>)), (vector_foo2, <8 x float>(<8 x float>, <8 x float> #0))] Notes about step 2: Given the fact that the external globals that the FE have generated are removed _before_ the vectorizer kicks in, I am not sure if the "attribute #0" needed for one of the parameter is still present at this point. IF NOT, I think that in this case we could enrich the "VectorFnInfo" as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; std::set<unsigned, llvm:Attribute> Attrs; }; The field "Attrs" maps the position of the parameter with the correspondent llvm::Attribute present in the global variable. I have added this note for the sake of completeness. I *think* that we won't be needing this additional Attrs field: I have already shown in the llvm patch I submitted that the function type "survives" after the global gets removed, I don't see why the parameter attribute shouldn't survive too (last famous words?). ### Step 3 This step happens in the LoopVectorizer. The InnerLoopVectorizer queries the TargetLibraryInfo looking for a vectorized version of the function by scalar name and function signature with the following method: TargetLibraryInfo::isFunctionVectorizable(std::string ScalarName, FuncionType *FTy); This is done in a way similar to what my current llvm patch does: the loop vectorizer makes up the function signature it needs and look for it in the TLI. If a match is found, vectorization is possible. Right now the compiler is not aware of uniform/linear function attributes, but it still can refer to them in a target agnostic way, by using scalar signatures for the uniform ones and using llvm::Attributes for the linear ones. Notice that the vector name here is not used at all, which is good as any architecture can come up with it's own name mangling for vector functions, without breaking the ability of the vectorizer to vectorize the same code with the new name mangling. ## External libraries vs user provided code The example with "pow" and "foo" I have provided before shows a function declaration and a function definition. Although the TLII mechanism I have described seems to be valid only for the former case, I think that it is valid also for the latter. In fact, in case of a function definition, the compiler would have to generate also the body of the vector function, but that external global variable could still be used to inform the TLII of such function. The fact that the vector function needed by the vectorizer is in some module instead of in an external library doesn't seems to make all that difference at compile time to me. # Some final notes (call for ideas!) There is one level of target dependence that I still have to sort out, and for this I need input from the community and in particular from the Intel folks. I will start with this example: #pragma omp declare simd float foo(float x); In case of NEON, this would generate 2 globals, one for vectors holding 2 floats, and one for vector holding 4 floats, corresponding to NEON 64-bit and 128-bit respectively. This means that the vectorizer have a unique function it could choose from the list the TLI provides. This is not the same on Intel, for example when this code generates vector names for AVX and AVX2. The register width for these architecture extensions are the same, so all the TLI has is a mapping between scalar name and (vectro_name, function_type) who's two elements differ only in the vector_name string. This breaks the target independence of the vectorizer, as it would require it to parse the vector_name to be able to choose between the AVX or the AVX2 implementation. Now, to make this work one should have to encode the SSE/SSE2/AVX/AVX2 information in the VectorFnInfo structure. Does anybody have an idea on how best to do it? For the sake of keeping the vectorizer target independent, I would like to avoid encoding this piece of information in the VectorFnInfo struct. I have seen that in your code you are generating SSE/AVX/AVX2/AVX512 vector functions, how do you plan to choose between them in the vectorizer? I could not find how you planned to solve this problem in your proposal, or have I just missed it? Is there a way to do this in the TLII? The function type of the vector function could use the "target-feature" attribute of function definitions, but how coudl the vectorizer decide which one to use? Anyway, that's it. Your feedback will be much appreciated. Cheers, Francesco ________________________________________ From: Tian, Xinmin <xinmin.tian at intel.com> Sent: 30 November 2016 17:16:12 To: Francesco Petrogalli; llvm-dev at lists.llvm.org Cc: nd; Masten, Matt; Hal Finkel; Zaks, Ayal Subject: RE: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Francesco, Good to know, you are working on the support for this feature. I assume you knew the RFC below. The VectorABI mangling we proposed were approved by C++ Clang FE name mangling owner David M from Google, the ClangFE support was committed in its main trunk by Alexey. "Proposal for function vectorization and loop vectorization with function calls", March 2, 2016. Intel Corp. http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html. Matt submitted patch to generate vector variants for function definitions, not just function declarations. You may want to take a look. Ayal's RFC will be also needed to support vectorization of function body in general. I agreed, we should have an option -fopenmp-simd to enable SIMD only, both GCC and ICC have similar options. I would suggest we shall sync-up on these work, so we don't duplicate the effort. Thanks, Xinmin -----Original Message----- From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of Francesco Petrogalli via llvm-dev Sent: Wednesday, November 30, 2016 7:11 AM To: llvm-dev at lists.llvm.org Cc: nd <nd at arm.com> Subject: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Dear all, I have just created a couple of differential reviews to enable the vectorisation of loops that have function calls to routines marked with "#pragma omp declare simd". They can be (re)viewed here: * https://reviews.llvm.org/D27249 * https://reviews.llvm.org/D27250 The current implementation allows the loop vectorizer to generate vector code for source file as: #pragma omp declare simd double f(double x); void aaa(double *x, double *y, int N) { for (int i = 0; i < N; ++i) { x[i] = f(y[i]); } } by invoking clang with arguments: $> clang -fopenmp -c -O3 file.c [...] Such functionality should provide a nice interface for vector libraries developers that can be used to inform the loop vectorizer of the availability of an external library with the vector implementation of the scalar functions in the loops. For this, all is needed to do is to mark with "#pragma omp declare simd" the function declaration in the header file of the library and generate the associated symbols in the object file of the library according to the name scheme of the vector ABI (see notes below). I am interested in any feedback/suggestion/review the community might have regarding this behaviour. Below you find a description of the implementation and some notes. Thanks, Francesco ----------- The functionality is implemented as follow: 1. Clang CodeGen generates a set of global external variables for each of the function declarations marked with the OpenMP pragma. Each of such globals are named according a mangling that is generated by llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the associated vector function. (See examples in the tests of the clang patch. Each scalar function can generate multiple vector functions depending on the clauses of the declare simd directives) 2. When clang created the TLII, it processes the llvm::Module and finds out which of the globals of the module have the correct mangling and type so that they be added to the TLII as a list of vector function that can be associated to the original scalar one. 3. The LoopVectorizer looks for the available vector functions through the TLII not by scalar name and vectorisation factor but by scalar name and vector function signature, thus enabling the vectorizer to be able to distinguish a "vector vpow1(vector x, vector y)" from a "vector vpow2(vector x, scalar y)". (The second one corresponds to a "declare simd uniform(y)" for a "scalar pow(scalar x, scalar y)" declaration). (Notice that the changes in the loop vectorizer are minimal.) Notes: 1. To enable SIMD only for OpenMP, leaving all the multithread/target behaviour behind, we should enable this also with a new option: -fopenmp-simd 2. The AArch64 vector ABI in the code is essentially the same as for the Intel one (apart from the prefix and the masking argument), and it is based on the clauses associated to "declare simd" in OpenMP 4.0. For OpenMP4.5, the parameters section of the mangled name should be updated. This update will not change the vectorizer behaviour as all the vectorizer needs to detect a vectorizable function is the original scalar name and a compatible vector function signature. Of course, any changes/updates in the ABI will have to be reflected in the symbols of the binary file of the library. 3. Whistle this is working only for function declaration, the same functionality can be used when (if) clang will implement the declare simd OpenMP pragma for function definitions. 4. I have enabled this for any loop that invokes the scalar function call, not just for those annotated with "#pragma omp for simd". I don't have any preference here, but at the same time I don't see any reason why this shouldn't be enabled by default for non annotated loops. Let me know if you disagree, I'd happily change the functionality if there are sound reasons behind that. _______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev --------------------------------------------------------------------- Intel Israel (74) Limited This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.
Francesco Petrogalli via llvm-dev
2016-Dec-08 16:05 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Hi Saher, Thanks for your email. Please see my comments below. On 08/12/2016 11:48, "Odeh, Saher" <saher.odeh at intel.com> wrote:>Hi Francesco, > >As you stated in the RFC, when vectorizing a scalar function (e.g. when >using omp declare simd), one needs to incorporate attributes to the >resulting vectorized-function. >These attributes describe a) the behavior of the function, e.g. mask-able >or not, and b) the type of the parameters, e.g. scalar or linear or any >other option.No, the attributes are needed only for the different linear clauses, not for (not)inbranch or uniform, as follows: 1. Regular vector parameter -> rendered as a vector in the function signature 2. Linear parameter -> rendered as a vector in the function signature, plus an attribute describing the kind of linearity, attached to the vector function signature parameter linear(i) -> linear = 1 linear(i:2) -> linear = 2 linear(i:c) uniform(c) ->linear_var = X, X being the position of the c parameter in the original function linear(ref(i):3) -> linear_ref = 3 linear(ref(I):c) uniform(c) -> linear_ref_var = X, X as before linear(val(i):3) -> linear_val = 3 linear(val(I):c) uniform(c) -> linear_val_var = X, X as before linear(uval(i):3) -> linear_uval = 3 linear(uval(I):c) uniform(c) -> linear_uval_var = X, X as before This means that I am asking to have 7 additional attributes, with an explicit meaning. 3. Uniform parameters -> kept as scalar parameters in the vector function signature I have a solution also for the (not)inbranch clause: just add an additional vector parameter representing the vector predicate. Here are some more examples for the cases I haven’t considered in the RFC #pragma omp declare simd inbranch int doit(float) vector signature for a 128 bit vector (the CDT here is int): <4 x i64> (<4 x i64>, <4 x float>) (notice the additional mask parameter)> >As this list is extensive, it is only logical to use an existing >infrastructure of ICC and GCC vectorABI which already covers all of these >options as stated in Xinmin's RFC >[http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html].The list is made by 7 attributes. They are gonna be present in the IR attribute only if used, and no more than 1 per function directive. This is smaller than the list of strings carrying all the possible names for the architecture.>Moreover, when considering other compilers such as GCC, I do see that the >resulting assembly actually does incorporate this exact infrastructure.I haven’t changed the ABI. The ABI produced (for Intel) is still the same. You can check this in test/OpenMP/declare_simd_no_definition.c, line 24-34 of https://reviews.llvm.org/D27250. All I want to achieve here is the vectorizer uses OpenMP declare simd information in a target independent way.> >So if we wish to link different parts of the program using clang and GCC >we'll need to adhere to the same name mangling/ABI. Please see the belowDefinitely. As I said, the name mangling I am producing is the same on x86.> >result after compiling an omp declare simd function using GCC. >Lastly, please note the two out of the three components of the >implementation have already been committed or submitted, and both are >adhering the name mangling proposed by Xinmin's RFC. A) committed - the >FE portion by Alexey [https://reviews.llvm.org/rL264853], it generates >mangled names in the manner described by Xinmin's RFC, See below B) >Submitted - the callee side by Matt [https://reviews.llvm.org/D22792], it >uses these mangled names. and C) caller which is covered by this patch.Yes, I understand. Nevertheless, I think that this approach it too X86 centric. Matt patch could be simplified with the method I am proposing, because inside the same target architecture his patch would have to look only at the part of the naming that relates to the particular vector extension (like “b” “c” “e” “d” for X86). Other architecture will take care of their own vector extension token in the naming. This is a possible solution to the “calls for ideas” I added and the end of my last email. I believe that this could be solve by attaching the already available “target-features” attribute to the function signature. I will update the proposal with this bits. Also, you mentioned gcc. Gcc is already gone further the name mangling you are proposing, as you can see here (short url https://goo.gl/WIfzQo): https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/omp-simd-clone.c;h=df140d40 446df013d25bcde6f13af82bf00c8f47;hb=HEAD#l331 This gcc patch contemplates all the possible combination of the linear clauses, which have been extended I openmp4.5. So the idea of using attributes would abstract this from the name mangling.> >In order to mitigate the needed effort and possible issues when >implementing, I believe it is best to follow the name mangling proposed >in Xinmin's RFC. What do you think?I think I stated my disagreement about the name mangling in this email. As I said, the name mangling works for X86, not for other architectures. I believe that each architecture is free to chose whatever name mangling they prefer. My example patches make the vectorizer independent of the architectural name mangling, which I think is a good approach as it could be shared by all architecture, avoiding the need to create custom code in the vectorizer - other than the target-feature attribute for the function signature. Cheers, Francesco> >GCC Example >---------------- >Compiler version: GCC 6.1.0 >Compile line: gcc -c omp.c -fopenmp -Wall -S -o - -O3 > omp.s > >omp.c >#include <omp.h> > >#pragma omp declare simd >int dowork(int* a, int idx) >{ > return a[idx] * a[idx]*7; >} > >less omp.s | grep @function > .type dowork, @function > .type _ZGVbN4vv_dowork, @function > .type _ZGVbM4vv_dowork, @function > .type _ZGVcN4vv_dowork, @function > .type _ZGVcM4vv_dowork, @function > .type _ZGVdN8vv_dowork, @function > .type _ZGVdM8vv_dowork, @function > .type _ZGVeN16vv_dowork, @function > .type _ZGVeM16vv_dowork, @function > >Clang on FE using Alexey's patch >--------------------------------------- >Compile line: clang -c tst/omp_fun.c -fopenmp -mllvm -print-after-all >& >out > >#pragma omp declare simd >extern int dowork(int* a, int idx) >{ > return a[idx]*7; >} > > >int main() { > dowork(0,1); >} > >attributes #0 = { nounwind uwtable "_ZGVbM4vv_dowork" "_ZGVbN4vv_dowork" >"_ZGVcM8vv_dowork" "_ZGVcN8vv_dowork" "_ZGVdM8vv_dowork" >"_ZGVdN8vv_dowork" "_ZGVeM16vv_dowork" "_ZGVeN16vv_dowork" >"correctly-rounded-divide-sqrt-fp-math"="false" >"disable-tail-calls"="false" "less-precise-fpmad"="false" >"no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" >"no-infs-fp-math"="false" "no-jump-tables"="false" >"no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" >"no-trapping-math"="false" "stack-protector-buffer-size"="8" >"target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" >"unsafe-fp-math"="false" "use-soft-float"="false" } > > >Thanks Saher > >-----Original Message----- >From: Francesco Petrogalli [mailto:Francesco.Petrogalli at arm.com] >Sent: Tuesday, December 06, 2016 17:22 >To: Tian, Xinmin <xinmin.tian at intel.com>; llvm-dev at lists.llvm.org >Cc: nd <nd at arm.com>; Masten, Matt <matt.masten at intel.com>; Hal Finkel ><hfinkel at anl.gov>; Zaks, Ayal <ayal.zaks at intel.com>; a.bataev at hotmail.com >Subject: Re: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the >LoopVectorizer > >Hi Xinmin, > >Thank you for your email. > >I have been catching up with the content of your proposal, and I have >some questions/remarks below that I'd like to discuss with you - see the >final section in the proposal. > >I have specifically added Alexey B. to the mail so we can move our >conversation from phabricator to the mailing list. > >Before we start, I just want to mention that the initial idea of using >llvm::FunctionType for vector function generation and matching has been >proposed by a colleague, Paul Walker, when we first tried out supporting >this on AArch64 on an internal version of llvm. I received some input >also from Amara Emerson. > >In our case we had a slightly different problem to solve: we wanted to >support in the vectorizer a rich set of vector math routines provided >with an external library. We managed to do this by adding the pragma to >the (scalar) function declaration of the header file provided with the >library, and as shown by the patches I have submitted, by generating >vector function signatures that the vectorizer can search in the >TargetLibraryInfo. > >Here is an updated version of the proposal. Please let me know what you >think, and if you have any solution we could use for the final section. > ># RFC for "pragma omp declare simd" > >Hight level components: > >A) Global variable generator (clang FE) >B) Parameter descriptors (as new enumerations in llvm::Attribute) >C) TLII methods and fields for the multimap (llvm middle-end) > >## Workflow > >Example user input, with a declaration and definition: > > #pragma omp declare simd > #pragma omp declare simd uniform(y) > extern double pow(double x, double y); > > #pragma omp declare simd > #pragma omp declare simd linear(x:2) > float foo(float x) {....} > > /// code using both functions > >### Step 1 > > >The compiler FE process these definition and declaration and generates a >list of globals as follows: > > @prefix_vector_pow1_midfix_pow_postfix = external global > <4 x double>(<4 x double>, > <4 x double>) > @prefix_vector_pow2_midfix_pow_postfix = external global > <4 x double>(<4 x double>, > double) > @prefix_vector_foo1_midfix_foo_postfix = external global > <8 x float>(<8 x float>, > <8 x float>) > @prefix_vector_foo1_midfix_foo_postfix = external global > <8 x float>(<8 x float>, > <8 x float> #0) > ... > attribute #0 = {linear = 2} > > >Notes about step 1: > >1. The mapping scalar name <-> vector name is in the > prefix/midfix/postfix mangled name of the global variable. >2. The examples shows only a set of possible vector function for a > sizeof(<4 x double>) vector extension. If multiple vector extension > live in the same target (eg. NEON 64-bit or NEON 128-bit, or SSE > and AVX512) the front end takes care to generate each of the > associated functions (like it is done now). >3. Vector function parameters are rendered using the same > Characteristic Data Type (CDT) rule already in the compiler FE. >4. Uniform parameters are rendered with the original scalar type. >5. Linear parameters are rendered with vectors using the same > CDT-generated vector length, and decorated with proper > attributes. I think we could extent the llvm::Attribute enumeration >adding the following: > - linear : numeric, specify_the step > - linear_var : numeric, specify the position of the uniform variable >holding the step > - linear_uval[_var]: numeric as before, but for the "uval" modifier >(both constant step or variable step) > - linear_val[_var]: numeric, as before, but for "val" modifier > - linear_ref[_var] numeric, for "ref" modifier. > > For example, "attribute #0 = {linear = 2}" says that the vector of > the associated parameter in the function signature has a linear > step of 2. > >### Step 2 > >The compiler FE invokes a TLII method in BackendUtils.cpp that populate a >multimap in the TLII by checking the globals created in the previous step. > >Each global is processed, demangling the [pre/mid/post]fix name and >generate a mapping in the TLII as follows: > > struct VectorFnInfo { > std::string Name; > FunctionType *Signature; > }; > std::multimap<std:string, VectorFnInfo> VFInfo; > > >For the initial example, the multimap in the TLI is populated as follows: > > "pow" -> [(vector_pow1, <4 x double>(<4 x double>, <4 x double>)), > (vector_pow2, <4 x double>(<4 x double>, double))] > > "foo" -> [(vector_foo1, <8 x float>(<8 x float>, <8 x float>)), > (vector_foo2, <8 x float>(<8 x float>, <8 x float> #0))] > >Notes about step 2: > >Given the fact that the external globals that the FE have generated are >removed _before_ the vectorizer kicks in, I am not sure if the "attribute >#0" needed for one of the parameter is still present at this point. IF >NOT, I think that in this case we could enrich the "VectorFnInfo" as >follows: > > struct VectorFnInfo { > std::string Name; > FunctionType *Signature; > std::set<unsigned, llvm:Attribute> Attrs; > }; > >The field "Attrs" maps the position of the parameter with the >correspondent llvm::Attribute present in the global variable. > >I have added this note for the sake of completeness. I *think* that we >won't be needing this additional Attrs field: I have already shown in the >llvm patch I submitted that the function type "survives" after the global >gets removed, I don't see why the parameter attribute shouldn't survive >too (last famous words?). > >### Step 3 > >This step happens in the LoopVectorizer. The InnerLoopVectorizer queries >the TargetLibraryInfo looking for a vectorized version of the function by >scalar name and function signature with the following method: > > TargetLibraryInfo::isFunctionVectorizable(std::string ScalarName, >FuncionType *FTy); > >This is done in a way similar to what my current llvm patch does: the >loop vectorizer makes up the function signature it needs and look for it >in the TLI. If a match is found, vectorization is possible. Right now the >compiler is not aware of uniform/linear function attributes, but it still >can refer to them in a target agnostic way, by using scalar signatures >for the uniform ones and using llvm::Attributes for the linear ones. > >Notice that the vector name here is not used at all, which is good as any >architecture can come up with it's own name mangling for vector >functions, without breaking the ability of the vectorizer to vectorize >the same code with the new name mangling. > >## External libraries vs user provided code > >The example with "pow" and "foo" I have provided before shows a function >declaration and a function definition. Although the TLII mechanism I have >described seems to be valid only for the former case, I think that it is >valid also for the latter. In fact, in case of a function definition, >the compiler would have to generate also the body of the vector function, >but that external global variable could still be used to inform the TLII >of such function. The fact that the vector function needed by the >vectorizer is in some module instead of in an external library doesn't >seems to make all that difference at compile time to me. > ># Some final notes (call for ideas!) > >There is one level of target dependence that I still have to sort out, >and for this I need input from the community and in particular from the >Intel folks. > >I will start with this example: > > #pragma omp declare simd > float foo(float x); > >In case of NEON, this would generate 2 globals, one for vectors holding 2 >floats, and one for vector holding 4 floats, corresponding to NEON 64-bit >and 128-bit respectively. This means that the vectorizer have a unique >function it could choose from the list the TLI provides. > >This is not the same on Intel, for example when this code generates >vector names for AVX and AVX2. The register width for these architecture >extensions are the same, so all the TLI has is a mapping between scalar >name and (vectro_name, function_type) who's two elements differ only in >the vector_name string. > >This breaks the target independence of the vectorizer, as it would >require it to parse the vector_name to be able to choose between the AVX >or the AVX2 implementation. > >Now, to make this work one should have to encode the SSE/SSE2/AVX/AVX2 >information in the VectorFnInfo structure. Does anybody have an idea on >how best to do it? For the sake of keeping the vectorizer target >independent, I would like to avoid encoding this piece of information in >the VectorFnInfo struct. I have seen that in your code you are generating >SSE/AVX/AVX2/AVX512 vector functions, how do you plan to choose between >them in the vectorizer? I could not find how you planned to solve this >problem in your proposal, or have I just missed it? > >Is there a way to do this in the TLII? The function type of the vector >function could use the "target-feature" attribute of function >definitions, but how coudl the vectorizer decide which one to use? > >Anyway, that's it. Your feedback will be much appreciated. > >Cheers, >Francesco > >________________________________________ >From: Tian, Xinmin <xinmin.tian at intel.com> >Sent: 30 November 2016 17:16:12 >To: Francesco Petrogalli; llvm-dev at lists.llvm.org >Cc: nd; Masten, Matt; Hal Finkel; Zaks, Ayal >Subject: RE: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the >LoopVectorizer > >Hi Francesco, > >Good to know, you are working on the support for this feature. I assume >you knew the RFC below. The VectorABI mangling we proposed were approved >by C++ Clang FE name mangling owner David M from Google, the ClangFE >support was committed in its main trunk by Alexey. > >"Proposal for function vectorization and loop vectorization with function >calls", March 2, 2016. Intel Corp. >http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html. > >Matt submitted patch to generate vector variants for function >definitions, not just function declarations. You may want to take a look. > Ayal's RFC will be also needed to support vectorization of function body >in general. > >I agreed, we should have an option -fopenmp-simd to enable SIMD only, >both GCC and ICC have similar options. > >I would suggest we shall sync-up on these work, so we don't duplicate the >effort. > >Thanks, >Xinmin > >-----Original Message----- >From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of >Francesco Petrogalli via llvm-dev >Sent: Wednesday, November 30, 2016 7:11 AM >To: llvm-dev at lists.llvm.org >Cc: nd <nd at arm.com> >Subject: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the >LoopVectorizer > >Dear all, > >I have just created a couple of differential reviews to enable the >vectorisation of loops that have function calls to routines marked with >"#pragma omp declare simd". > >They can be (re)viewed here: > >* https://reviews.llvm.org/D27249 > >* https://reviews.llvm.org/D27250 > >The current implementation allows the loop vectorizer to generate vector >code for source file as: > > #pragma omp declare simd > double f(double x); > > void aaa(double *x, double *y, int N) { > for (int i = 0; i < N; ++i) { > x[i] = f(y[i]); > } > } > > >by invoking clang with arguments: > > $> clang -fopenmp -c -O3 file.c [...] > > >Such functionality should provide a nice interface for vector libraries >developers that can be used to inform the loop vectorizer of the >availability of an external library with the vector implementation of the >scalar functions in the loops. For this, all is needed to do is to mark >with "#pragma omp declare simd" the function declaration in the header >file of the library and generate the associated symbols in the object >file of the library according to the name scheme of the vector ABI (see >notes below). > >I am interested in any feedback/suggestion/review the community might >have regarding this behaviour. > >Below you find a description of the implementation and some notes. > >Thanks, > >Francesco > >----------- > >The functionality is implemented as follow: > >1. Clang CodeGen generates a set of global external variables for each of >the function declarations marked with the OpenMP pragma. Each of such >globals are named according a mangling that is generated by >llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the >associated vector function. (See examples in the tests of the clang patch. >Each scalar function can generate multiple vector functions depending on >the clauses of the declare simd directives) 2. When clang created the >TLII, it processes the llvm::Module and finds out which of the globals of >the module have the correct mangling and type so that they be added to >the TLII as a list of vector function that can be associated to the >original scalar one. >3. The LoopVectorizer looks for the available vector functions through >the TLII not by scalar name and vectorisation factor but by scalar name >and vector function signature, thus enabling the vectorizer to be able to >distinguish a "vector vpow1(vector x, vector y)" from a "vector >vpow2(vector x, scalar y)". (The second one corresponds to a "declare >simd uniform(y)" for a "scalar pow(scalar x, scalar y)" declaration). >(Notice that the changes in the loop vectorizer are minimal.) > > >Notes: > >1. To enable SIMD only for OpenMP, leaving all the multithread/target >behaviour behind, we should enable this also with a new option: >-fopenmp-simd >2. The AArch64 vector ABI in the code is essentially the same as for the >Intel one (apart from the prefix and the masking argument), and it is >based on the clauses associated to "declare simd" in OpenMP 4.0. For >OpenMP4.5, the parameters section of the mangled name should be updated. >This update will not change the vectorizer behaviour as all the >vectorizer needs to detect a vectorizable function is the original scalar >name and a compatible vector function signature. Of course, any >changes/updates in the ABI will have to be reflected in the symbols of >the binary file of the library. >3. Whistle this is working only for function declaration, the same >functionality can be used when (if) clang will implement the declare simd >OpenMP pragma for function definitions. >4. I have enabled this for any loop that invokes the scalar function >call, not just for those annotated with "#pragma omp for simd". I don't >have any preference here, but at the same time I don't see any reason why >this shouldn't be enabled by default for non annotated loops. Let me know >if you disagree, I'd happily change the functionality if there are sound >reasons behind that. > >_______________________________________________ >LLVM Developers mailing list >llvm-dev at lists.llvm.org >http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >--------------------------------------------------------------------- >Intel Israel (74) Limited > >This e-mail and any attachments may contain confidential material for >the sole use of the intended recipient(s). Any review or distribution >by others is strictly prohibited. If you are not the intended >recipient, please contact the sender and delete all copies.
Tian, Xinmin via llvm-dev
2016-Dec-08 18:11 UTC
[llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
Hi Francesco, a bit more information. GCC veclib is implemented based on GCC VectorABI for declare simd as well. For name mangling, we have to follow certain rules of C/C++ (e.g. prefix needs to _ZVG ....). David Majnemer who is the owner and stakeholder for approval for Clang and LLVM. Also, we need to pay attention to GCC compatibility. I would suggest you look into how GCC VectorABI can be extended support your Arch. Thanks, Xinmin -----Original Message----- From: Odeh, Saher Sent: Thursday, December 8, 2016 3:49 AM To: Tian, Xinmin <xinmin.tian at intel.com>; llvm-dev at lists.llvm.org; Francesco.Petrogalli at arm.com Cc: nd <nd at arm.com>; Masten, Matt <matt.masten at intel.com>; Hal Finkel <hfinkel at anl.gov>; Zaks, Ayal <ayal.zaks at intel.com>; a.bataev at hotmail.com Subject: RE: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Francesco, As you stated in the RFC, when vectorizing a scalar function (e.g. when using omp declare simd), one needs to incorporate attributes to the resulting vectorized-function. These attributes describe a) the behavior of the function, e.g. mask-able or not, and b) the type of the parameters, e.g. scalar or linear or any other option. As this list is extensive, it is only logical to use an existing infrastructure of ICC and GCC vectorABI which already covers all of these options as stated in Xinmin's RFC [http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html]. Moreover, when considering other compilers such as GCC, I do see that the resulting assembly actually does incorporate this exact infrastructure. So if we wish to link different parts of the program using clang and GCC we'll need to adhere to the same name mangling/ABI. Please see the below result after compiling an omp declare simd function using GCC. Lastly, please note the two out of the three components of the implementation have already been committed or submitted, and both are adhering the name mangling proposed by Xinmin's RFC. A) committed - the FE portion by Alexey [https://reviews.llvm.org/rL264853], it generates mangled names in the manner described by Xinmin's RFC, See below B) Submitted - the callee side by Matt [https://reviews.llvm.org/D22792], it uses these mangled names. and C) caller which is covered by this patch. In order to mitigate the needed effort and possible issues when implementing, I believe it is best to follow the name mangling proposed in Xinmin's RFC. What do you think? GCC Example ---------------- Compiler version: GCC 6.1.0 Compile line: gcc -c omp.c -fopenmp -Wall -S -o - -O3 > omp.s omp.c #include <omp.h> #pragma omp declare simd int dowork(int* a, int idx) { return a[idx] * a[idx]*7; } less omp.s | grep @function .type dowork, @function .type _ZGVbN4vv_dowork, @function .type _ZGVbM4vv_dowork, @function .type _ZGVcN4vv_dowork, @function .type _ZGVcM4vv_dowork, @function .type _ZGVdN8vv_dowork, @function .type _ZGVdM8vv_dowork, @function .type _ZGVeN16vv_dowork, @function .type _ZGVeM16vv_dowork, @function Clang on FE using Alexey's patch --------------------------------------- Compile line: clang -c tst/omp_fun.c -fopenmp -mllvm -print-after-all >& out #pragma omp declare simd extern int dowork(int* a, int idx) { return a[idx]*7; } int main() { dowork(0,1); } attributes #0 = { nounwind uwtable "_ZGVbM4vv_dowork" "_ZGVbN4vv_dowork" "_ZGVcM8vv_dowork" "_ZGVcN8vv_dowork" "_ZGVdM8vv_dowork" "_ZGVdN8vv_dowork" "_ZGVeM16vv_dowork" "_ZGVeN16vv_dowork" "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } Thanks Saher -----Original Message----- From: Francesco Petrogalli [mailto:Francesco.Petrogalli at arm.com] Sent: Tuesday, December 06, 2016 17:22 To: Tian, Xinmin <xinmin.tian at intel.com>; llvm-dev at lists.llvm.org Cc: nd <nd at arm.com>; Masten, Matt <matt.masten at intel.com>; Hal Finkel <hfinkel at anl.gov>; Zaks, Ayal <ayal.zaks at intel.com>; a.bataev at hotmail.com Subject: Re: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Xinmin, Thank you for your email. I have been catching up with the content of your proposal, and I have some questions/remarks below that I'd like to discuss with you - see the final section in the proposal. I have specifically added Alexey B. to the mail so we can move our conversation from phabricator to the mailing list. Before we start, I just want to mention that the initial idea of using llvm::FunctionType for vector function generation and matching has been proposed by a colleague, Paul Walker, when we first tried out supporting this on AArch64 on an internal version of llvm. I received some input also from Amara Emerson. In our case we had a slightly different problem to solve: we wanted to support in the vectorizer a rich set of vector math routines provided with an external library. We managed to do this by adding the pragma to the (scalar) function declaration of the header file provided with the library, and as shown by the patches I have submitted, by generating vector function signatures that the vectorizer can search in the TargetLibraryInfo. Here is an updated version of the proposal. Please let me know what you think, and if you have any solution we could use for the final section. # RFC for "pragma omp declare simd" Hight level components: A) Global variable generator (clang FE) B) Parameter descriptors (as new enumerations in llvm::Attribute) C) TLII methods and fields for the multimap (llvm middle-end) ## Workflow Example user input, with a declaration and definition: #pragma omp declare simd #pragma omp declare simd uniform(y) extern double pow(double x, double y); #pragma omp declare simd #pragma omp declare simd linear(x:2) float foo(float x) {....} /// code using both functions ### Step 1 The compiler FE process these definition and declaration and generates a list of globals as follows: @prefix_vector_pow1_midfix_pow_postfix = external global <4 x double>(<4 x double>, <4 x double>) @prefix_vector_pow2_midfix_pow_postfix = external global <4 x double>(<4 x double>, double) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float>) @prefix_vector_foo1_midfix_foo_postfix = external global <8 x float>(<8 x float>, <8 x float> #0) ... attribute #0 = {linear = 2} Notes about step 1: 1. The mapping scalar name <-> vector name is in the prefix/midfix/postfix mangled name of the global variable. 2. The examples shows only a set of possible vector function for a sizeof(<4 x double>) vector extension. If multiple vector extension live in the same target (eg. NEON 64-bit or NEON 128-bit, or SSE and AVX512) the front end takes care to generate each of the associated functions (like it is done now). 3. Vector function parameters are rendered using the same Characteristic Data Type (CDT) rule already in the compiler FE. 4. Uniform parameters are rendered with the original scalar type. 5. Linear parameters are rendered with vectors using the same CDT-generated vector length, and decorated with proper attributes. I think we could extent the llvm::Attribute enumeration adding the following: - linear : numeric, specify_the step - linear_var : numeric, specify the position of the uniform variable holding the step - linear_uval[_var]: numeric as before, but for the "uval" modifier (both constant step or variable step) - linear_val[_var]: numeric, as before, but for "val" modifier - linear_ref[_var] numeric, for "ref" modifier. For example, "attribute #0 = {linear = 2}" says that the vector of the associated parameter in the function signature has a linear step of 2. ### Step 2 The compiler FE invokes a TLII method in BackendUtils.cpp that populate a multimap in the TLII by checking the globals created in the previous step. Each global is processed, demangling the [pre/mid/post]fix name and generate a mapping in the TLII as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; }; std::multimap<std:string, VectorFnInfo> VFInfo; For the initial example, the multimap in the TLI is populated as follows: "pow" -> [(vector_pow1, <4 x double>(<4 x double>, <4 x double>)), (vector_pow2, <4 x double>(<4 x double>, double))] "foo" -> [(vector_foo1, <8 x float>(<8 x float>, <8 x float>)), (vector_foo2, <8 x float>(<8 x float>, <8 x float> #0))] Notes about step 2: Given the fact that the external globals that the FE have generated are removed _before_ the vectorizer kicks in, I am not sure if the "attribute #0" needed for one of the parameter is still present at this point. IF NOT, I think that in this case we could enrich the "VectorFnInfo" as follows: struct VectorFnInfo { std::string Name; FunctionType *Signature; std::set<unsigned, llvm:Attribute> Attrs; }; The field "Attrs" maps the position of the parameter with the correspondent llvm::Attribute present in the global variable. I have added this note for the sake of completeness. I *think* that we won't be needing this additional Attrs field: I have already shown in the llvm patch I submitted that the function type "survives" after the global gets removed, I don't see why the parameter attribute shouldn't survive too (last famous words?). ### Step 3 This step happens in the LoopVectorizer. The InnerLoopVectorizer queries the TargetLibraryInfo looking for a vectorized version of the function by scalar name and function signature with the following method: TargetLibraryInfo::isFunctionVectorizable(std::string ScalarName, FuncionType *FTy); This is done in a way similar to what my current llvm patch does: the loop vectorizer makes up the function signature it needs and look for it in the TLI. If a match is found, vectorization is possible. Right now the compiler is not aware of uniform/linear function attributes, but it still can refer to them in a target agnostic way, by using scalar signatures for the uniform ones and using llvm::Attributes for the linear ones. Notice that the vector name here is not used at all, which is good as any architecture can come up with it's own name mangling for vector functions, without breaking the ability of the vectorizer to vectorize the same code with the new name mangling. ## External libraries vs user provided code The example with "pow" and "foo" I have provided before shows a function declaration and a function definition. Although the TLII mechanism I have described seems to be valid only for the former case, I think that it is valid also for the latter. In fact, in case of a function definition, the compiler would have to generate also the body of the vector function, but that external global variable could still be used to inform the TLII of such function. The fact that the vector function needed by the vectorizer is in some module instead of in an external library doesn't seems to make all that difference at compile time to me. # Some final notes (call for ideas!) There is one level of target dependence that I still have to sort out, and for this I need input from the community and in particular from the Intel folks. I will start with this example: #pragma omp declare simd float foo(float x); In case of NEON, this would generate 2 globals, one for vectors holding 2 floats, and one for vector holding 4 floats, corresponding to NEON 64-bit and 128-bit respectively. This means that the vectorizer have a unique function it could choose from the list the TLI provides. This is not the same on Intel, for example when this code generates vector names for AVX and AVX2. The register width for these architecture extensions are the same, so all the TLI has is a mapping between scalar name and (vectro_name, function_type) who's two elements differ only in the vector_name string. This breaks the target independence of the vectorizer, as it would require it to parse the vector_name to be able to choose between the AVX or the AVX2 implementation. Now, to make this work one should have to encode the SSE/SSE2/AVX/AVX2 information in the VectorFnInfo structure. Does anybody have an idea on how best to do it? For the sake of keeping the vectorizer target independent, I would like to avoid encoding this piece of information in the VectorFnInfo struct. I have seen that in your code you are generating SSE/AVX/AVX2/AVX512 vector functions, how do you plan to choose between them in the vectorizer? I could not find how you planned to solve this problem in your proposal, or have I just missed it? Is there a way to do this in the TLII? The function type of the vector function could use the "target-feature" attribute of function definitions, but how coudl the vectorizer decide which one to use? Anyway, that's it. Your feedback will be much appreciated. Cheers, Francesco ________________________________________ From: Tian, Xinmin <xinmin.tian at intel.com> Sent: 30 November 2016 17:16:12 To: Francesco Petrogalli; llvm-dev at lists.llvm.org Cc: nd; Masten, Matt; Hal Finkel; Zaks, Ayal Subject: RE: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Hi Francesco, Good to know, you are working on the support for this feature. I assume you knew the RFC below. The VectorABI mangling we proposed were approved by C++ Clang FE name mangling owner David M from Google, the ClangFE support was committed in its main trunk by Alexey. "Proposal for function vectorization and loop vectorization with function calls", March 2, 2016. Intel Corp. http://lists.llvm.org/pipermail/cfe-dev/2016-March/047732.html. Matt submitted patch to generate vector variants for function definitions, not just function declarations. You may want to take a look. Ayal's RFC will be also needed to support vectorization of function body in general. I agreed, we should have an option -fopenmp-simd to enable SIMD only, both GCC and ICC have similar options. I would suggest we shall sync-up on these work, so we don't duplicate the effort. Thanks, Xinmin -----Original Message----- From: llvm-dev [mailto:llvm-dev-bounces at lists.llvm.org] On Behalf Of Francesco Petrogalli via llvm-dev Sent: Wednesday, November 30, 2016 7:11 AM To: llvm-dev at lists.llvm.org Cc: nd <nd at arm.com> Subject: [llvm-dev] [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer Dear all, I have just created a couple of differential reviews to enable the vectorisation of loops that have function calls to routines marked with "#pragma omp declare simd". They can be (re)viewed here: * https://reviews.llvm.org/D27249 * https://reviews.llvm.org/D27250 The current implementation allows the loop vectorizer to generate vector code for source file as: #pragma omp declare simd double f(double x); void aaa(double *x, double *y, int N) { for (int i = 0; i < N; ++i) { x[i] = f(y[i]); } } by invoking clang with arguments: $> clang -fopenmp -c -O3 file.c [...] Such functionality should provide a nice interface for vector libraries developers that can be used to inform the loop vectorizer of the availability of an external library with the vector implementation of the scalar functions in the loops. For this, all is needed to do is to mark with "#pragma omp declare simd" the function declaration in the header file of the library and generate the associated symbols in the object file of the library according to the name scheme of the vector ABI (see notes below). I am interested in any feedback/suggestion/review the community might have regarding this behaviour. Below you find a description of the implementation and some notes. Thanks, Francesco ----------- The functionality is implemented as follow: 1. Clang CodeGen generates a set of global external variables for each of the function declarations marked with the OpenMP pragma. Each of such globals are named according a mangling that is generated by llvm::TargetLibraryInfoImpl (TLII), and holds the vector signature of the associated vector function. (See examples in the tests of the clang patch. Each scalar function can generate multiple vector functions depending on the clauses of the declare simd directives) 2. When clang created the TLII, it processes the llvm::Module and finds out which of the globals of the module have the correct mangling and type so that they be added to the TLII as a list of vector function that can be associated to the original scalar one. 3. The LoopVectorizer looks for the available vector functions through the TLII not by scalar name and vectorisation factor but by scalar name and vector function signature, thus enabling the vectorizer to be able to distinguish a "vector vpow1(vector x, vector y)" from a "vector vpow2(vector x, scalar y)". (The second one corresponds to a "declare simd uniform(y)" for a "scalar pow(scalar x, scalar y)" declaration). (Notice that the changes in the loop vectorizer are minimal.) Notes: 1. To enable SIMD only for OpenMP, leaving all the multithread/target behaviour behind, we should enable this also with a new option: -fopenmp-simd 2. The AArch64 vector ABI in the code is essentially the same as for the Intel one (apart from the prefix and the masking argument), and it is based on the clauses associated to "declare simd" in OpenMP 4.0. For OpenMP4.5, the parameters section of the mangled name should be updated. This update will not change the vectorizer behaviour as all the vectorizer needs to detect a vectorizable function is the original scalar name and a compatible vector function signature. Of course, any changes/updates in the ABI will have to be reflected in the symbols of the binary file of the library. 3. Whistle this is working only for function declaration, the same functionality can be used when (if) clang will implement the declare simd OpenMP pragma for function definitions. 4. I have enabled this for any loop that invokes the scalar function call, not just for those annotated with "#pragma omp for simd". I don't have any preference here, but at the same time I don't see any reason why this shouldn't be enabled by default for non annotated loops. Let me know if you disagree, I'd happily change the functionality if there are sound reasons behind that. _______________________________________________ LLVM Developers mailing list llvm-dev at lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Apparently Analagous Threads
- [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
- [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
- [RFC] Enable "#pragma omp declare simd" in the LoopVectorizer
- Proposal for function vectorization and loop vectorization with function calls
- Proposal for function vectorization and loop vectorization with function calls