cpointersassemblyembeddedcortex-a8

Pointer subtraction, 32-bit ARM, negative distance reported as postive


When performing subtraction of pointers and the first pointer is less than the second, I'm getting an underflow error with the ARM processor.

Example code:

#include <stdint.h>
#include <stdbool.h>

uint8_t * p_formatted_data_end;
uint8_t   formatted_text_buffer[10240];

static _Bool
Flush_Buffer_No_Checksum(void)
{
    _Bool       system_failure_occurred = false;
    p_formatted_data_end = 0; // For demonstration puposes.
    const signed int  length =
        p_formatted_data_end - &formatted_text_buffer[0];
    if (length < 0)
    {
        system_failure_occurred = true;
    }
    //...
    return true;
}

The assembly code generated by the IAR compiler is:

    807          static _Bool
    808          Flush_Buffer_No_Checksum(void)
    809          {
   \                     Flush_Buffer_No_Checksum:
   \   00000000   0xE92D4070         PUSH     {R4-R6,LR}
   \   00000004   0xE24DD008         SUB      SP,SP,#+8
    810             _Bool       system_failure_occurred = false;
   \   00000008   0xE3A04000         MOV      R4,#+0
    811             p_formatted_data_end = 0; // For demonstration purposes.
   \   0000000C   0xE3A00000         MOV      R0,#+0
   \   00000010   0x........         LDR      R1,??DataTable3_7
   \   00000014   0xE5810000         STR      R0,[R1, #+0]
    812              const signed int  length =
    813                 p_formatted_data_end - &formatted_text_buffer[0];
   \   00000018   0x........         LDR      R0,??DataTable3_7
   \   0000001C   0xE5900000         LDR      R0,[R0, #+0]
   \   00000020   0x........         LDR      R1,??DataTable7_7
   \   00000024   0xE0505001         SUBS     R5,R0,R1
    814             if (length < 0)
   \   00000028   0xE3550000         CMP      R5,#+0
   \   0000002C   0x5A000009         BPL      ??Flush_Buffer_No_Checksum_0
    815              {
    816                  system_failure_occurred = true;
   \   00000030   0xE3A00001         MOV      R0,#+1
   \   00000034   0xE1B04000         MOVS     R4,R0

The subtraction instruction SUBS R5,R0,R1 is equivalent to:

R5 = R0 - R1

The N bit in the CPSR register will be set if the result is negative. Ref: Section A4.1.106 SUB of ARM Architecture Reference Manual

Let:

R0 == 0x00000000
R1 == 0x802AC6A5

Register R5 will have the value 0x7FD5395C. The N bit of the CPSR register is 0, indicating the result is not negative.

The Windows 7 Calculator application is reporting negative, but only when expressed as 64-bits: FFFFFFFF7FD5395C.

As an experiment, I used the ptrdiff_t type for the length, and the same assembly language was generated.

Questions:

  1. Is this valid behavior, to have the result of pointer subtraction to underflow?
  2. What is the recommended data type to view the distance as negative?

Platform:
Target Processor: ARM Cortex A8 (TI AM3358)
Compiler: IAR 7.40
Development platform: Windows 7.


Solution

  • Is this valid behavior, to have the result of pointer subtraction to underflow?

    Yes, because the behavior in your case is undefined. Any behavior is valid there. As was observed in comments, the difference between two pointers is defined only for pointers that point to elements of the same array object, or one past the last element of the array object (C2011, 6.5.6/9).

    What is the recommended data type to view the distance as negative?

    Where it is defined, the result of subtracting two pointers is specified to be of type ptrdiff_t, a signed integer type of implementation-defined size. If you evaluate p1 - p2, where p1 points to an array element and p2 points to a later element of the same array, then the result will be a negative number representable as a ptrdiff_t.