This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler not saving registered variables

Sorry, this is probably a really stupid noob question. I'm just spinning up a lm3s1b21 design and working on an I2C driver. This is spaghetti code using the Stellarisware library (no OS).

What Ive found is that some local variables are being stored in processor registers. This is an automatic occurance and not something I specified in the declaration. The value of these registered variables are not being pushed onto the stack and restored when sub functions are called. As such the variables are are obliterated when the functions return.

Any suggestions?

Thank you

-Gary

 

  • The C compiler will place variables in registers when possible instead of wasting stack space for every local variable.  This is a basic optimization.

    Assembly functions called by C code must obey the C calling conventions.  Part of the C calling conventions specify which function is responsible for saving and restoring each register.  The called function is responsible for saving and restoring the "callee-saved" registers if it modifies them.   The calling function must expect that any other registers ("caller-saved") are not preserved by the callee, and must take care to save and restore them itself.  See the Compiler User's Guide (see http://processors.wiki.ti.com/index.php/Before_asking_for_CGT_support for a link) section 6.5 "Interfacing C and C++ With Assembly Language" for details on which register is which.

  • There are no assembly functions being called. When I referred to what was happening under the hood, I was describing the disassebled code produced by the compiler.

    Function 1 declares an array of unsigned characters then initializes a few values and passes the address to function 2. In the disassembled code, the call uses register 2.

    function 2 declares a local unsigned char pointer which is assigned to the address of the array. Then function2 calls a stellarisware function, the return value of which should be placed in the array using the character pointer.  but on return from the stellarisware call, R2 which contained the pointer, has been corrupted.

     

  • Does function 1 use the value of R3 after the call to function 2?  If not, there is no need to save and restore it.

    R2 is a "caller-saved" register.  If function 1 needs the value of R2, it needs to save R2 before the call to function 2.  Even if function 1 knows that function 2 doesn't write to R2, function 2 still calls a function which could (and apparently does) write to R2.  Was function 1 generated by the compiler?  Could you post a test case (including compiler version and command-line options) that demonstrates the problem? 

  • Will do as soon as I get a chance.

    For now I'm declaring the pointer as static, which seems to force the compiler to use a memory location rather than a register at the function 1 level.

    Thank you

    -Gary

  • I am having this same problem.  I have previously forwarded all of my code to our TI Account Manager for a previous compiler problem I was having.

    I have asked him to ask the compile team to to examime this new problem.

    Basically R0 is not being saved in my GetCurrentTaskProperties() routine which is called from a higher MutexLock() routine, and the address to the mutex which is in R0 gets corrupted, so the MutexLock() hangs forever, waiting for an invalid mutex(the corrupted R0) to be released.

     

    void GAClk_SetGPSPwr(RT_UINT mode)
    {
      MutexLock(&GPS_pwr_mutex);                                                   /* stored in R0 */
      if (mode == 0 && GPS_power == 1)
    .
    .
    .
    void MutexLock(mutex_type *this_mutex)
    {
      TASK_PROPS *task_entry;
      task_entry = GetCurrentTaskProperties();
    #if DBG_MUTEX
      if (task_entry)UARTprintf("\r\nTASK %s getting mutex %d", task_entry->name, this_mutex);
    #endif

      /* first lock the mutex */
      while (WidgetMutexGet(this_mutex))                          /* hangs here because R0 has invalid address */
      {
        if (task_entry)
          xTaskDelay(1);
        else
        {
          UARTDumpRawMsg("\r\nMutex Lock by non task!!!");
          return;/* avoid infinite loop & just give access*/
        }
      }
    #if DBG_MUTEX
      if (task_entry)UARTprintf("\r\nTASK %s got mutex %d", task_entry->name, this_mutex);
    #endif

      /*then make sure we do not create a possible priority inversion problem*/
    #ifdef FIX_PRIORITY_INVERSION
      if (task_entry)
        xTaskPrioritySet(task_entry->handle, RT160_MAX_TASK_PRI);
    #endif
    }

    ...
    xTaskHandle xTaskGetCurrentTaskHandle(void)
    {
      TASK_PROPS *task_entry;
      signed portCHAR * stack_ptr;
      int x;

      /* look thru the task stacks defined and see which task ths is */
      task_entry = rt160_tasks;                                                                     /* stored in R0 which is never pushed onto stack */
      stack_ptr = (signed portCHAR * )&task_entry;
      while (task_entry->task_fnc != NULL)
      {
        if (stack_ptr >= task_entry->stack
            && stack_ptr <= (task_entry->stack + task_entry->stack_size))
        {
    #if 0
          Con_Msg(1, "\r\n%s", task_entry->name);
    #endif
          return(*task_entry->handle);
        }
        task_entry++;
      }

    Any ideas on compiler options I can manupulate to ensure code get compiler properly?

    Roberta

     

  • After moving the order of functions around in the routine I found that if I moved the following routine with inline assembly to the bottom of the file, the MutexLock saved the register OK & I was able to progress...

    #if !defined (ccs)
    unsigned long __attribute__((naked)) WidgetMutexGet(unsigned char *pcMutex)
    {
      unsigned long ulRet;
    #else
    unsigned long WidgetMutexGet(unsigned char *pcMutex)
    {
    #endif

      //
      // Acquire the mutex if possible.
      //
      __asm("    mov      r1, #1\n"
            "    ldrexb   r2, [r0]\n"
            "    cmp      r2, #0\n"
            "    it EQ      \n"
            "    strexbeq r2, r1, [r0]\n"
            "    mov      r0, r2\n"
            "    bx       lr\n"
    #if !defined (ccs)
            : "=r" (ulRet));

      //
      // The return is handled in the inline assembly, but the compiler will
      // still complain if there is not an explicit return here (despite the fact
      // that this does not result in any code being produced because of the
      // naked attribute).
      //
      return(ulRet)
    #else
    #pragma diag_suppress=994
    )
    #endif