Project - Stage 2 (Part 1)

In SPO600 project - Stage 2, it is divided into 2 parts:

(1) examine the intermediate code output of the compiler

(2) propose a design for how the automatic ifunc capability could operate from the user's point of view

I will work on the part 1 in this post.


1. Unpack the file

First of all, run cd ; tar xvf /public/spo600-autoifunc-preprocessor-pos.tgz to make sure you have the unpacked file in your directory.


2. Build the code

What does the scripts/autoifunc script or program do?

It utilizes the GCC compiler to automatically build functions as Indirect Functions (ifunc). ifunc is a mechanism used to selectively support specific features at runtime.
Certainly, let's take a brief look at a few lines of code related to the functionality of the script:

The script uses the makeheaders command to generate a .h file from a .c file for the purpose of obtaining function prototypes. 
    ...
    makeheaders "${TEMP_C}.c"
    ...

The generated resolver function utilizes the `getauxval` function to check hardware support and return the appropriate function. 
    ...
    echo "static void (*${FUNC_NAME}__resolver(void)) {
            long hwcaps  = getauxval(AT_HWCAP);
            long hwcaps2 = getauxval(AT_HWCAP2);
    ... 

Let's build the code now. You can get the function_ifunc.c file after you run the make command.
Use the GCC compiler to build your source code.
Below are the outputs of function.c and function_ifunc.c .

    $ ./function 
    Usage: ./function input.jpg red green blue output.jpg count
    Where red/green/blue are in the range 0.0-2.0


    $ ./function_ifunc 
    Usage: ./function_ifunc input.jpg red green blue output.jpg count
    Where red/green/blue are in the range 0.0-2.0


As you can see, the executed file name is updated.

1) Resolver function

We can find the adjust_channels__resolver function in the function_ifunc.c file.

    static void (*adjust_channels__resolver(void)) {
            long hwcaps  = getauxval(AT_HWCAP);
            long hwcaps2 = getauxval(AT_HWCAP2);


     if (hwcaps & HWCAP_SVE) {
                    return adjust_channels__sve;
            } else {
                    return adjust_channels__asimd;
            }

    };


It returns the two different copies of the adjust_channels functions based on the architecture.
One of them is adjust_channels__sve, which supports SVE(Scalable Vector Extensions). Otherwise, it returns adjust_channels__asimd.

2) Pragma statements

#pragma GCC target \"arch=armv8-a+sve2\"

#pragma GCC target \"arch=armv8-a+sve\"

#pragma GCC target \"arch=armv8-a\"

These three statements instruct the GCC compiler to apply optimizations tailored for different ARM architectures.
The first directive introduces a set of vectorized instructions designed for parallel computations, while the second one facilitates support for diverse vector operations. The third one enables general optimizations customized for the ARMv8-A architecture.

3) Indirect function prototype

void adjust_channels(unsigned char *image, int x_size, int y_size, 
float red_factor, float green_factor, float blue_factor);

It defines the function's name, paramaeters and the return type.

3. Add the compiler option -fdump-tree-gimple

After I run the code with -fdump-tree-gimple, I got the files that ends with .006t.gimple.

4. What is gimple?

GIMPLE is a three-address representation used in the GCC compiler, derived from GENERIC. It simplifies GENERIC expressions into tuples with a maximum of 3 operands, influenced by the McCAT compiler project's SIMPLE IL. The 'gimplifier' recursively converts GENERIC into GIMPLE tuples, introducing temporaries for intermediate values.

GIMPLE breaks down control structures, converting them into conditional jumps, and simplifies lexical scopes and exception regions. Initially implemented using internal data structures for parse trees, GIMPLE's representation is stored in the DECL_SAVED_TREE field. The C and C++ front ends directly convert to GIMPLE, bypassing conversion to GENERIC.

You can request a C-like representation of GIMPLE with the flag -fdump-tree-gimple during compilation.

4-1. Examin the gimple file for the function_ifunc.c source file

1)  Are there any differences between the SVE and ASIMD versions of the function?

        -    adjust_channels__sve: Utilizes the SVE (Scalable Vector Extension) SIMD instruction set, providing efficient processing of vectorized data in ARM architecture.
 
        -    adjust_channels__asimd: Uses the ASIMD (Advanced SIMD) instruction set, an older SIMD extension in ARM architecture, enabling parallel processing of vectorized operations on data.


2)  Has autovectorization been applied to the code at this poing in the compilation process? How do you know?

Determining whether autovectorization has been applied depends on the compilation settings and flags used during the compilation process. 
To ascertain whether autovectorization has been applied, you can check the compiler flags such as -fno-tree-vectorize, -02 or -03. Additionally, compiler diagnostic messages (e.g., -ftree-vectorizer-verbose=2) can provide information on vectorization decisions made during compilation.

3)  How are the #pragma lines in the source file reflected in the gimple?

The #pragma lines are used to specify the target architecture for compilation.

#pragma GCC target "arch=armv8-a+sve"

  • It was reflected in __attribute__((target ("arch=armv8-a+sve"))) in the gimple file.

#pragma GCC target "arch=armv8-a"

  • It was reflected in __attribute__((target ("arch=armv8-a+sve", "arch=armv8-a"))) in the gimple file.


5. Figure out what would be required to transform the gimple representation of function.c into the gimple represenation of function_ifunc.c

To transform the GIMPLE representation of function.c into the GIMPLE representation of function_ifunc.c, you would need to make corresponding modifications:


-  IFUNC (Indirect Function) Related Code Addition: 
The adjust_channels__resolver function serves as the IFUNC resolver function, checking SVE support and returning the appropriate function pointer.

-  Addition of SVE and ARMv8-A Specific Code:
The functions adjust_channels__sve and adjust_channels__asimd target SVE and ARMv8-A architectures, respectively. The function which returns them properly should be added.

-  Header File Inclusion:
Header files like #include <sys/auxv.h> are necessary when using SVE-related functionalities.

Comments

Popular posts from this blog

Understanding Docker: A Comprehensive Guide

Project - Stage 1