Project Stage2 - Part I
Tags: spo600
Published: 2023-11-08 20:44:24

**1.Obtain the autoifunc proof-of-conccept code**
`tar xvf /public/spo600-autoifunc-preprocessor-pos.tgz`

```shell
[hchoi72@israel autoifunc-preprocessor-poc]$ ll
total 56
-rw-r--r--. 1 hchoi72 hchoi72   212 Nov  8 09:59 adjust_channels.h
-rw-r--r--. 1 hchoi72 hchoi72  1310 Nov  8 09:59 function.c
-rw-r--r--. 1 hchoi72 hchoi72   197 Nov  8 09:59 function.h
-rw-r--r--. 1 hchoi72 hchoi72 18092 Nov  8 09:59 LICENSE
-rw-r--r--. 1 hchoi72 hchoi72  2448 Nov  8 09:59 main.c
-rw-r--r--. 1 hchoi72 hchoi72   941 Nov  8 10:42 Makefile
-rw-------. 1 hchoi72 hchoi72     0 Nov  8 09:59 nohup.out
-rw-r--r--. 1 hchoi72 hchoi72   571 Nov  8 10:17 README.md
drwxr-xr-x. 2 hchoi72 hchoi72  4096 Nov  8 10:11 scripts
drwxr-xr-x. 2 hchoi72 hchoi72  4096 Nov  8 09:59 tar
drwxr-xr-x. 4 hchoi72 hchoi72  4096 Nov  8 09:59 tests
```

**2.Build the code**
`$ make`
After building the code, `function_ifunc.c` is generated.

```shell
[hchoi72@israel autoifunc-preprocessor-poc]$ ll
total 788
...
-rw-r--r--. 1 hchoi72 hchoi72   3224 Nov  8 13:22 function_ifunc.c
...
```

**_Compilation_**
`gcc function.c -o function`
`gcc function_ifunc.c -o function_ifunc`
`./function`
`./function_ifunc`

**_Resolver function_**
`adjust_channels__resolver`
- determines which version of `adjust_channels` function will be used based on the architecture
- checks if SVE is supported. If it does, it returns a pointer to `adjust_channels__sve`. If it doesn't, it returns a pointer to `adjust_channels__asimd`

**_Two different copies of `adjust_channels function`_**
1.SVE Implementation - `adjust_channels__sve`
- uses Scalable Vector Extension instructions

2.ASIMD Implementation - `adjust_channels__asimd`
- uses advanced SIMD instructions

**_Pragma statements_**
`#pragma GCC target "arch=armv8-a+sve"`
`#pragma GCC target "arch=armv8-a"`
- specifies which architecture to be used for the compilation
- first pragma will use armv8-a+sve and second pragma will use armv8-a

**_Indirect function prototype_**
`void adjust_channels(unsigned char *image,int x_size,int y_size,float red_factor,float green_factor,float blue_factor) **__attribute__(( ifunc("adjust_channels__resolver"))**);`
- attributes specifies function implementation is going to be resolved at runtime depending on the result of `adjust_channels__resolver`

**3.Add the compiler option -fdump-tree-gimple**
`gcc function.c -o function -fdump-tree-gimple`
`gcc function_ifunc.c -o function_ifunc -fdump-tree-gimple`

-> generate output files with the extension `.gimple` in the same directory. Output files contain the gimple representation of the code.

```shell
[hchoi72@israel autoifunc-preprocessor-poc]$ ll
total 120
...
-rw-r--r--. 1 hchoi72 hchoi72  2026 Nov  8 14:09 function.c.006t.gimple
...
-rw-r--r--. 1 hchoi72 hchoi72  5337 Nov  8 14:09 function_ifunc.c.006t.gimple
...
```

**4.GIMPLE**
**_what is [GIMPLE](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html)?_**
- stands for GCC Intermediate Representation
- simplified version of code, which makes the code easier to analyze and manipulate for optimization

**_Examine `function_ifunc.c.006t.gimple`_**

**_Are there any differences between the SVE and ASIMD versions of the function?_**
- `adjust_channels__sve` vs `adjust_channels__asimd`
- Main difference: How it performs the calculations
- Overall structure and logic are very similar

**_Has autovectorization been applied to the code at this poing in the compilation process?_**
- autovectorization: compiler optimization technique by automatically transforming code into SIMD code using vectorized instruction
- since autovectorization happens at later stage of compilation process, it is not very clear whether autovectorization has been applied

**_How do you know then?_**
- check whether compiler flags are following: `-02`, `-03` or `-ftree-vectorize`., etc
- check if `-fopt-info`flag was specified in an optimization report
- check out the assembly code to see whether any SIMD instructions are used

**_How are the #pragma lines in the source file reflected in the gimple?_**
`#pragma GCC target "arch=armv8-a+sve"`
- reflected as `__attribute__((target ("arch=armv8-a+sve")))` in the gimple
- it's applied to function like `void adjust_channels__sve()` or `main()` as these functions are intneded for the ARMv8-A architecture with SVE

`#pragma GCC target "arch=armv8-a"`
- reflected as `__attribute__((target ("arch=armv8-a+sve", "arch=armv8-a")))` in the gimple

**5.Steps to transform the gimple representation of function.c into the gimple represenation of function_ifunc.c**

**_Main difference between `function.c.006t.gimple` vs 
`function_ifunc.c.006t.gimple`_**
- use SVE instruction in `function.c.006t.gimple`
- use indirect function and both SVE and ASIMD instruction in `function_ifunc.c.006t.gimple`

**_Steps_**
- include headers
`#include <sys/auxv.h>`
- add indirect function declaration in `function.c`

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

_after_
```shell
void adjust_channels(unsigned char *image,int x_size,int y_size,float red_factor,float green_factor,float blue_factor) __attribute__(( ifunc("adjust_channels__resolver")));
```

- add Ifunc resolver function, which decides which version of `adjust_channels` to be used
```shell
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;
        }
};
```