Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

C6000 Compiler Obscure Restrict Bug

From Texas Instruments Wiki
Jump to: navigation, search

Introduction

The C6000 C Compiler has a bug related to the use of restrict pointers inside loops. This bug rarely affects generated code in practice. This article describes this bug, and what to do about it.

Bug Description

Consider this code fragment.

// Almost sorts, but not really
void almost_sorts(int * restrict base, int length)
{
   int i;
 
   for (i = 0; i < length-1; i++)
   {
      if (base[i] < base[i+1])
      {
         int tmp;
         int * restrict p1 = &base[i];
         int * restrict p2 = &base[i+1];
 
         tmp = *p1;
         *p1 = *p2;
         *p2 = tmp;
      }
   }
}

This is correct usage of the restrict keyword. During execution of that innermost block where they are declared, the pointers p1 and p2 never point to the same memory address. The article Restrict Type Qualifier has more detail on restrict.

But recall that the C6000 compiler, to maximize performance, may overlap loop iterations using a technique called software pipelining. Details can be found in Hand-Tuning Loops and Control Code on the TMS320C6000 tidoc:spra666, in the first part of section 4. Thus, p2 of loop iteration N and p1 of loop iteration N+1 could point to the same memory location. Is this correct usage of the restrict keyword? Yes. It is up to the compiler to detect this possible aliasing and schedule the loop accordingly. Within a loop iteration, no aliasing is possible. Across loop iterations, aliasing is possible.

For this example the compiler correctly assumes there is no aliasing within a loop iteration. However, the compiler does not account for possible aliasing among loop iterations. Thus the compiler may emit code which executes incorrectly.

Restrict in Loop Usually Arises from Function Inlining

Actual instances of this bug causing incorrect code to be generated are quite rare. That is because of the precise manner in which the restrict must be used to cause the problem. The example above is written to clearly illustrate the problem. Here is a code fragment which shows how the problem more typically appears.

   do
   {
      changed = FALSE;
      for (i=0; i < length-1; i++)
      {
         if(ary[i].size < ary[i+1].size)
         {
            swap(&ary[i], &ary[i+1]);  // gets inlined
            changed = TRUE;
         }
      }
   } while (changed);
 
// ...
 
static inline void swap (struct sss * restrict p1,
                         struct sss * restrict p2)
{
   // swap contents of the two structs
}

Some of the fields in certain structs in the array get corrupted. Note how the restrict does not appear in the main part of the example, but comes in because the compiler inlines the function swap.

Workaround

Workaround this problem by not using the restrict keyword in the problem loop. This makes the performance of the loop worse; sometimes much worse. But at least the impact is limited to one loop.

Version Information

This bug is present in all 6.0.x and 6.1.x versions of the compiler. It is fixed in versions 7.0.x and later.