Pointer Aliasing and Vectorization

Published:04/24/2019   Last Updated:04/24/2019

Introduction

Pointer aliasing in C++ inhibits vectorization and other optimizations, and, hence, performance. When the compiler has some help to know that the referenced memory locations are unique, a number of optimizations can be enabled including vectorization.

This article discusses the rules of pointer aliasing and remedies for violations.

  • What aliasing rules are all about?
  • How they can be violated?
  • How to fix a violation?
  • How to enable support in the compiler?
  • What performance improvement may occur?
  • Conclusions

What aliasing rules are all about?

The rules regarding aliasing are described in the C++ Standard.

1. The stored value of an object can be accessed by an lvalue (locator value) of types when

  • Compatible with the effective type of object
  • A qualified (const, volatile, …) AND signed/unsigned version of compatible type
  • An aggregate or union type that includes aforementioned types as members

2. Pointers of different types (say int* and float*) can’t point to the same object. For example,

  int a = 20; 
  int* pa = &a; 
  unsigned int* pua = (unsigned int* )&a;  // pua is aliasing to pa

3. Access to a field of a structure cannot alias with access to another field of the same structure

4. There are exceptions: char*

In this example, only 2 memory accesses are able to be aliased.

   typedef struct { short field1; } S1;
   typedef struct { double field1; double field2; } S2;
   void foo( int *p1, float *p2, S1 *p3, S1 *p4, S2 *p5)
   {
     *p1 = 10;           // cannot alias
     *p2 = 20.0;         // cannot alias
     p3->field1 = 30;    // can alias
     p4->field1 = 40;    // can alias
     p5->field1 = 50.0;  // cannot alias
     p5->field2 = 60.0;  // cannot alias
   }

How aliasing rules can be violated?

“Type punning” is defined as a violation of the aliasing rules based on the assumption that two different data types have the same representation. One example is pointer type and an integral types. Another is floating point types and integral type; these are very often in math libraries.

This is not a good programming practice. Here is an example of poor coding practice from the SPEC* CPU2006 benchmark 403.gcc, varasm.c:3723:

    int *strp; struct rtx_const value;
     strp = (int *) &value;
     while (--len >= 0)
      if (*p++ != *strp++)

How to fix a violation?

Do you really have to have to access a particular data object using different types? It's better not to!

But, if you must, then use union or char*. For example, using union, in the previous example from 403.gcc:

    union { int strp[]; struct rtx_const value; } foo;
    int* pv = foo.strp;
    ...
    while (--len >= 0)
     if (*p++ != *pv++)    // considered 2 fields of a structure. (See rule 3 above.)

A more comprehensive fix is to compare corresponding fields of the struct.

How to enable support in the compiler?

Use the -ansi-alias compiler option to tell the Intel* C++ compiler that the C++ program adheres to the C++ standard. It is the default when compiling with -O2. Using this option enables the compiler to more aggressively optimize the code including vectorization.  

Let the developer beware If a code is compiled with this option and does not follow the rules, the compiler may generate incorrect code.

For the Intel C++ compiler, the default for ansi-alias varies with the OS. It is easy for the developer to change the default.

  • On Windows*, the default disables the ANSI aliasing rules in optimizations.

     /Qansi-alias tells the compiler to assume compliance 

     /Qansi-alias- tells the compiler the code is not in compliance

  • On Linux* and macOS*, the default enables the ANSI aliasing rules in optimizations.

     -ansi-alias tells the compiler to assume compliance

     -no-ansi-alias tells the compiler the code is not in compliance.
    
A useful compiler option is the -ansi-alias-check option that enables the ansi-alias checker.  The ansi-alias checker reviews the source code for potential violations of ANSI aliasing rules and disables unsafe optimizations related to the code for those statements that are identified as potential violations.  Use this option to identify potential violations.  However, ansi-alias checker may not catch all violations. This option is disabled by default.

What performance improvement may occur?

The performance improvement that may be achieved varies by the application and the specific compiler options used to compile the code.  With the Intel compiler, it is important to indicate if the application complies with the ANSI aliasing rules, if 

  •   -ipo is not being used or 
  •   the whole program is not available or 
  •   the application has many pointer references.

Conclusions

  • Use -ansi-alias!
  • If you have a violation, use char* and unions to resolve it
  • Or better yet, avoid access to the same data object using different types
  • Enjoy performance benefits!

Product and Performance Information

1

Performance varies by use, configuration and other factors. Learn more at www.Intel.com/PerformanceIndex.