6 Replies Latest reply on Oct 11, 2017 12:35 AM by DheerajDake

    Compiler optimization in PSOC Creator 4.1 - Debug vs Release

    DheerajDake

      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

        • 1. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
          ntan

          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

          • 2. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
            DheerajDake

            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

            • 3. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
              ntan

              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

              1 of 1 people found this helpful
              • 4. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
                DheerajDake

                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

                • 5. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
                  ntan

                  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

                  1 of 1 people found this helpful
                  • 6. Re: Compiler optimization in PSOC Creator 4.1 - Debug vs Release
                    DheerajDake

                    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