SPO600 Project : Stage 2
    In this stage, I will examine the intermediate code output and propose a design for how the automatic ifunc capability could operate from a user's POV.
Part 1: Experiment with the Proof-of-Concept
In this part, I will be experimenting with the Proof-of-Concept Automatic ifunc tool and the intermediate Representation produced by the GCC compiler.
Unpacking and building the code
First, I obtained the autoifunc proof-of-concept code from the file path provided by our professor. I then unpacked the tar file into my own directory.
I then build the code code using $make
After building the code, function_ifunc.c is generated.
Now, I will be comparing the output of function_ifunc.cand function.c
Compiling function.c
Compiling function_ifunc.c
Identifying the resolver function
Identifying the 2 copies of adjust_channels function
Identifying the pragma statements
Identifying the indirect function prototype
Adding the compiler option -fdump-tree-gimple
- function-main.c.006t.gimple 
- function_ifunc-main.c.006t.gimple 
Researching about gimple
- What is gimple?
- Gimple is an intermediate representation of a program
- used for optimization purposes because it makes code easier to analyze and refactor
Examining the gimple file for function_ifunc.c
- Are there any differences between the SVE and ASIMD versions of the function?
void adjust_channels__sve (unsigned char * image, int x_size, int y_size, float red_factor, float green_factor, float blue_factor)
{
  unsigned char iftmp.0;
  unsigned char iftmp.1;
  unsigned char iftmp.2;
  {
    int i;
    i = 0;
    goto ;
    :
    _1 = (sizetype) i;
    _2 = image + _1;
    _3 = *_2;
    _4 = (float) _3;
    _5 = red_factor * _4;
    if (_5 < 2.55e+2) goto ; else goto ;
    :
    _6 = (sizetype) i;
    _7 = image + _6;
    _8 = *_7;
    _9 = (float) _8;
    _10 = red_factor * _9;
    iftmp.0 = (unsigned char) _10;
    goto ;
    :
    iftmp.0 = 255;
    :
    _11 = (sizetype) i;
    _12 = image + _11;
    *_12 = iftmp.0;
    _13 = (sizetype) i;
    _14 = _13 + 1;
    _15 = image + _14;
    _16 = *_15;
    _17 = (float) _16;
    _18 = blue_factor * _17;
    if (_18 < 2.55e+2) goto ; else goto ;
    :
    _19 = (sizetype) i;
    _20 = _19 + 1;
    _21 = image + _20;
    _22 = *_21;
    _23 = (float) _22;
    _24 = blue_factor * _23;
    iftmp.1 = (unsigned char) _24;
    goto ;
    :
    iftmp.1 = 255;
    :
    _25 = (sizetype) i;
    _26 = _25 + 1;
    _27 = image + _26;
    *_27 = iftmp.1;
    _28 = (sizetype) i;
    _29 = _28 + 2;
    _30 = image + _29;
    _31 = *_30;
    _32 = (float) _31;
    _33 = green_factor * _32;
    if (_33 < 2.55e+2) goto ; else goto ;
    :
    _34 = (sizetype) i;
    _35 = _34 + 2;
    _36 = image + _35;
    _37 = *_36;
    _38 = (float) _37;
    _39 = green_factor * _38;
    iftmp.2 = (unsigned char) _39;
    goto ;
    :
    iftmp.2 = 255;
    :
    _40 = (sizetype) i;
    _41 = _40 + 2;
    _42 = image + _41;
    *_42 = iftmp.2;
    i = i + 3;
    :
    _43 = x_size * y_size;
    _44 = _43 * 3;
    if (i < _44) goto ; else goto ;
    :
  }
}
                        
void adjust_channels__asimd (unsigned char * image, int x_size, int y_size, float red_factor, float green_factor, float blue_factor)
{
  unsigned char iftmp.3;
  unsigned char iftmp.4;
  unsigned char iftmp.5;
  {
    int i;
    i = 0;
    goto ;
    :
    _1 = (sizetype) i;
    _2 = image + _1;
    _3 = *_2;
    _4 = (float) _3;
    _5 = red_factor * _4;
    if (_5 < 2.55e+2) goto ; else goto ;
    :
    _6 = (sizetype) i;
    _7 = image + _6;
    _8 = *_7;
    _9 = (float) _8;
    _10 = red_factor * _9;
    iftmp.3 = (unsigned char) _10;
    goto ;
    :
    iftmp.3 = 255;
    :
    _11 = (sizetype) i;
    _12 = image + _11;
    *_12 = iftmp.3;
    _13 = (sizetype) i;
    _14 = _13 + 1;
    _15 = image + _14;
    _16 = *_15;
    _17 = (float) _16;
    _18 = blue_factor * _17;
    if (_18 < 2.55e+2) goto ; else goto ;
    :
    _19 = (sizetype) i;
    _20 = _19 + 1;
    _21 = image + _20;
    _22 = *_21;
    _23 = (float) _22;
    _24 = blue_factor * _23;
    iftmp.4 = (unsigned char) _24;
    goto ;
    :
    iftmp.4 = 255;
    :
    _25 = (sizetype) i;
    _26 = _25 + 1;
    _27 = image + _26;
    *_27 = iftmp.4;
    _28 = (sizetype) i;
    _29 = _28 + 2;
    _30 = image + _29;
    _31 = *_30;
    _32 = (float) _31;
    _33 = green_factor * _32;
    if (_33 < 2.55e+2) goto ; else goto ;
    :
    _34 = (sizetype) i;
    _35 = _34 + 2;
    _36 = image + _35;
    _37 = *_36;
    _38 = (float) _37;
    _39 = green_factor * _38;
    iftmp.5 = (unsigned char) _39;
    goto ;
    :
    iftmp.5 = 255;
    :
    _40 = (sizetype) i;
    _41 = _40 + 2;
    _42 = image + _41;
    *_42 = iftmp.5;
    i = i + 3;
    :
    _43 = x_size * y_size;
    _44 = _43 * 3;
    if (i < _44) goto ; else goto ;
    :
  }
}                        
- The major difference between the two functions is how each performs the calculations. The syntax and structure of the code between the functions are very similar.
- Has autovectorization been applied to the code at this point in the compilation process? How do you know?
- Autovectorization has not yet been applied to the code because there is no SIMD instructions used within the function
- How are the pragma lines in the source file reflected in the gimple?
- __attribute__((target ("arch=armv8-a+sve", "arch=armv8-a")))
- __attribute__((target ("arch=armv8-a+sve")))
- __attribute__((target ("arch=armv8-a+sve", "arch=armv8-a")))
- __attribute__((target ("arch=armv8-a+sve")))
Transforming the gimple representation of function.c into the gimple representation of function_ifunc.c
- Add the ifunc attribute to adjust_channels
Part 2: Designing an automatic ifunc implimentation
In this part, I was tasked to show how an automatic ifunc implementation within the GCC compiler could work from a users point-of-view.
1. To enable automatic ifunc implementation, the user needs to use compiler flags and options.
- A user for example can use the flag -fifunc to generate the code that automatically supports ifunc implementation.
- To specify the list of architecture variants the user wants to target, they can use the flags -march and -mtune. Some examples are:
-march=<baseArch> -mtune=<extentedArch_1>,<extendedArch_2
-march=armv8-a -mtune=armv8-a+sve,armv8-a+sve2
- There should be a detection mechanism at runtime to check if the targeted architecture variants are compatible
- The architecture variants should share a common base architecture and have compatible instruction sets
- Automatic ifunc capability should be added to functions that have multiple versions. In the case of the code example, the automatic ifunc capability can be added in the adjust_channels function as it is architecture-based
- There are many factors as to whether the user should explicitly specify the functions to be affected.
- functions that have multiple versions catered to specific architectures should automatically have automatic ifunc capabilities
- functions that are optimized for multiple architectures must also automatically have automatic ifunc capabilities
- library functions or frameworks can be user specified on the other hand
- A user can specify which functions should have automatic ifunc capabilities using the syntax:
__attribute__((ifunc("resolver_function")))void specified_function_1() {}
- A user should get a warning when the target architecture variants are incompatible
- A user should also get a warning statement if the target variants are not supported by the runtime environment
- A user should also get an error indicating syntax errors
- It is also beneficial for the user to receive informational messages that contain information about the compilation process, optimizations that were used, and information about the target architectures.
















Comments
Post a Comment