Compiler optimization in PSOC Creator 4.1 - Debug vs Release

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
DhDa_2432241
Level 5
Level 5
5 likes given First like received First like given

Hello all,

I have a function like this

void fn()

{

    char buf1[160] = {0};

    int count = 5;

    while(count > 0){

        char buf2[16] = {0};

        sprintf(buf2, "%s", "aabbccddeeff-123");  //buf2 has always 16 characters

        int len = strlen(buf1);

        printf("Len: %d\n", len);

        memcpy(&buf1[len], buf2, sizeof(buf2));

        count--;

    }

}

When debug mode is selected, the function works as expected.

Output:

Len 0

Len 16

Len 32

Len 48

Len 64

When release mode is selected, the output is

Len 0

Len 0

Len 0

Len 0

Len 0

I am not sure what's going on. If I make buf1 global variable, the function works as expected in release mode. What kind of optimization is the compiler doing? How do debug this?

Thank you

Dheeraj

0 Likes
6 Replies
NoriTan
Employee
Employee
25 sign-ins 5 questions asked 10 sign-ins

You are copying a 16 character string to the 16-bytes length buf2 array.

        sprintf(buf2, "%s", "aabbccddeeff-123");  //buf2 has always 16 characters

In this case, the length of the constant string "aabbccddeeff-123" is 16.  But the required area size is 17 including the NULL character indicating the end of the string.  Please refer following lines from the LST file.

132 0000 61616262 .ascii "aabbccddeeff-123\000"

132      63636464

132      65656666

132      2D313233

132      00

133              .LC2:

134 0011 4C656E3A .ascii "Len: %d\012\000"

134      2025640A

134      00

So, the sprintf() function copies 17 characters to the buf2 and the last NULL character is stored to the first byte of the buf1.  So, the string length of the buf1 is always ZERO.  The behavior in the Release mode is correct.  Please confirm the value of buf1[0] after the sprintf() execution.

The difference between these modes is the interpretation of the sprintf() function.

  54              .LVL2:

  22:main.c        ****         sprintf(buf2, "%s", "aabbccddeeff-123");  //buf2 has always 16 characters

  55              .loc 1 22 0

  56 001a 0B49      ldr r1, .L5

  57 001c 6846      mov r0, sp

  58 001e FFF7FEFF bl strcpy

In the "Release" mode the line is converted to a simple strcpy() function.  Total 17 characters are copied by this function.

  58              .LVL2:

  22:main.c        ****         sprintf(buf2, "%s", "aabbccddeeff-123");  //buf2 has always 16 characters

  59              .loc 1 22 0

  60 001c 0D4B      ldr r3, .L4

  61 001e 6A46      mov r2, sp

  62 0020 23CB      ldmia r3!, {r0, r1, r5}

  63 0022 23C2      stmia r2!, {r0, r1, r5}

  64 0024 1900      movs r1, r3

  65 0026 1B68      ldr r3, [r3]

  66 0028 1360      str r3, [r2]

  67 002a 0B79      ldrb r3, [r1, #4]

  68 002c 1371      strb r3, [r2, #4]

In the "Debug" mode the sprintf() is converted into a set of discrete op-codes.  The first 32*3 bits are copied via the {r0, r1, r5} registers.  The next 32-bit is copied via the r3 register and the last 17th byte is copied by r3. But the source address of the last byte is stored in r1 at the line 64 and the 13th byte (buf2[12]='-') is copied to the buf1[0]. So, it seems that the behavior is same as the "Release" mode.

I wonder why the compiler converts the sprintf() into such strange code.  Does anyone have any idea?

Regards,

Noriaki

Thank you for the reply. I had looked at LST files but I didn't know how to interpret them.

So, the sprintf() function copies 17 characters to the buf2 and the last NULL character is stored to the first byte of the buf1.

How is this possible? buf1 is declared before buf2 so it shouldn't be copied in buf1.

Can you explain how did you reach this statement?

The first 32*3 bits are copied via the {r0, r1, r5} registers.  The next 32-bit is copied via the r3 register and the last 17th byte is copied by r3. But the source address of the last byte is stored in r1 at the line 64 and the 13th byte (buf2[12]='-') is copied to the buf1[0].

Regards,

Dheeraj

0 Likes

I found incorrect description regarding your second question.  It seems the behavior is same in "Release" and "Debug" mode.  A debug session can be used why the buf1[0] is not destroyed in "Debug" mode, but I don't have any hardware right now.

The character array buf1[160] and buf2[16] are assigned to a memory area on the stack  because they are declared as local variables.

The buf2[16] is assigned at (sp+0) and the buf1[160] is assigned at (sp+16) where sp indicates the Stack Pointer pointing the top of the Stack area.

The memory area for buf2[] and buf1[] are assigned consecutively.  In addition the C specification does not detect the area overrun of an array.  So, if you access to the buf2's 17th byte (buf2[16]) you are accessing the buf1's first byte (buf1[0])  Please confirm the value of buf1[0] in a debugging session.

Regards,

Noriaki

Thank you. I get it. The stack grows downward. Yes, buf1[0] is null. I understood this part.

A debug session can be used why the buf1[0] is not destroyed in "Debug" mode, but I don't have any hardware right now.

I have CYBLE-212019 module. Any tips on how to debug this?

Regards,

Dheeraj

0 Likes

For example, the C-language specification does not have a boundary check of the array index.  So, it is recommended to check the index and the size if arrays explicitly with a macro CYASSERT.

    CYASSERT(sizeof buf1 >= strlen(buf1) + sizeof buf2 + 1);

        memcpy(&buf1[len], buf2, sizeof(buf2));

If the condition is FALSE, CYASSERT halts and debugger can recognize where the CPU stuck.  In addition, CYASSERT macro is disabled in Release mode not to increase the program code size.

Regards,

Noriaki

Regarding the difference between debug mode and release mode,

If I replace the line

        sprintf(buf2, "%s", "aabbccddeeff-123");  //buf2 has always 16 characters

with memcpy(buf2, "aabbccddeeff-123", 16);

This doesn't copy the NULL character to buf1[0]. The output works as expected. From the LST output mentioned above, I think the debug mode is using the instructions for memcpy instead of strcpy for sprintf().

Regards,

Dheeraj

0 Likes