Uninitialised Variables, Select, Optimisation

Technical questions regarding the XTC tools and programming with XMOS.
Post Reply
gcore
Member
Posts: 11
Joined: Thu Oct 06, 2016 4:58 am

Uninitialised Variables, Select, Optimisation

Post by gcore »

I've encountered some interesting behaviour with uninitialised variables when they are only used within select, and when optimisation is enabled in the compiler.

Code: Select all

#include <syscall.h>
#include <platform.h>
#include <xs1.h>
#include <xclib.h>
#include <print.h>

#include "debug_print.h"

// Change either of these to 1 to avoid the issue.
#define USE_VARIABLE_BEFORE_SELECT 0            // Using the variable before the select is enough to cause it to not be optimised away.
#define LOGICAL_NOT_VARIABLE_EXPLICITLY 0       // Assigning a non-optimised-away value explicitly to the variable is enough to cause it to not be optimised away.  

void Test(void)
{
    timer tmr;
    int t;
    tmr :> t;

    // Uninitialised variable.
    unsigned variable;

#if USE_VARIABLE_BEFORE_SELECT
    // Simply using the variable before the select is enough to cause it to not be optimised away.
    debug_printf("Before Select: initial value of variable = 0x%x\n", variable);
    variable = !variable;
    debug_printf("Before Select: final value of variable = 0x%x\n", variable);
#endif

    while (1)
    {
        select
        {
            case tmr when timerafter(t) :> int tmp:
            {
                // Come back in 1 second.
                t += 100000000;

                // Printing (reading) the variable is not enough to avoid it being optimised away.
                debug_printf("In Select: initial value of variable = 0x%x\n", variable);

#if LOGICAL_NOT_VARIABLE_EXPLICITLY
                // Toggling the variable explicitly within the select is enough to cause it to not be optimised away.
                if (variable > 0)
                {
                    variable = 0;
                }
                else
                {
                    variable = 1;
                }
#else
                // Operating on the variable within select using only the variable as input is not enough to avoid the variable being optimised away.
                variable = !variable;
#endif
                debug_printf("In Select: final value of variable = 0x%x\n", variable);
            }
            break;
        }
    }
}

int main(void)
{
    par
    {
        Test();
    }
    return 0;
}
Compiling with the default optimization level -O2, and running, I see the following output from the debug_printf statements:
In Select: initial value of variable = 0x7FF7C
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x1FFFFFF
In Select: final value of variable = 0x0
Each time the timer fires, I would expect the variable to toggle between 0 and 1, no matter what value it started at, for an output like this:
In Select: initial value of variable = 0x0
In Select: final value of variable = 0x1
In Select: initial value of variable = 0x1
In Select: final value of variable = 0x0
In Select: initial value of variable = 0x0
In Select: final value of variable = 0x1
In Select: initial value of variable = 0x1
In Select: final value of variable = 0x0
Problem is, it seems the variable doesn't actually have its own reserved place in memory, and each time the timer fires, the variable reads as something other than what it was set to last time the timer fired.

There are a number ways I've found to work around the issue:
1. Compiling with optimisation disabled.
2. Initialising the variable.
3. Using the variable outside of the select, even the same operation that doesn't work within select.
4. Assigning something to the variable other than something derived purely from the variable.

But, none of these should need to be done. Optimisation shouldn't affect the output of the program. Given that performing the same variable = !variable operation outside of the select is enough to cause the variable to be not optimised away, I would hope that that would also be enough if performed within the select. I welcome any comments, especially any suggestions from the folks at XMOS.


gcore
Member
Posts: 11
Joined: Thu Oct 06, 2016 4:58 am

Post by gcore »

I should mention that I'm at xTIMEcomposer version 14.2.4.
robertxmos
XCore Addict
Posts: 169
Joined: Fri Oct 23, 2015 10:23 am

Post by robertxmos »

hi gcore,

There is a bug in the XC compiler (and in the C compiler).
It seems that uninitialised variables that are variadic argument (to printf type functions) are not counted as 'used', and can be optimises away.
I believe this is an issue with the llvm backend and will be fixed when we pull from upstream.
The fix is to use initialised variables... see why below!

Further more the result of:

Code: Select all

  if (variable > 0)
and

Code: Select all

  variable = !variable;
on uninitialised variables is undefined, and the compiler will do as it wishes.
The result is that the first printf prints the 'undef' value.
The second '0' due to the compiler choosing to simplify the undefined-if-statement to variable = 0;
This is because it has already decided what ever happens is undefined and all subsequent passes would be defined in terms of the initial undefined choice!

Thus the llvm optimiser spits out (look at the trialing args):

Code: Select all

  %3 = tail call i32 (i8*, ...)* @iprintf(i8* getelementptr inbounds ([45 x i8]* @.str, i32 0, i32 0), i32 undef)
  %4 = tail call i32 (i8*, ...)* @iprintf(i8* getelementptr inbounds ([43 x i8]* @.str1, i32 0, i32 0), i32 0)
robert
Post Reply