C28 EABI: missing DP load caused by incorrect size of struct with flexible arrays

XMLWordPrintable

    • Type: Bug
    • Resolution: Fixed
    • Priority: Medium
    • Code Generation Tools
    • CODEGEN-15499
    • Show
      https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1640197/tms320f28379d-compiler-gets-the-address-of-struct-member-wrong
    • Hide
      C2000_22.6.0.LTS
      C2000_25.11.0.LTS
      C2000_21.6.0.LTS
      C2000_18.12.0.LTS
      C2000_20.2.0.LTS
      Show
      C2000_22.6.0.LTS C2000_25.11.0.LTS C2000_21.6.0.LTS C2000_18.12.0.LTS C2000_20.2.0.LTS
    • Hide
      C2000_22.6.4.LTS
      C2000_25.11.2.LTS*
      Show
      C2000_22.6.4.LTS C2000_25.11.2.LTS*
    • default
    • Hide
      Place each struct with flexible array members into their own sections
            __attribute__((section(".data:str_with_fam")))

      Replace the flexible array member with a fixed-size array
      Show
      Place each struct with flexible array members into their own sections       __attribute__((section(".data:str_with_fam"))) Replace the flexible array member with a fixed-size array

      For the C28 compiler with EABI, the DP load insertion pass fails to emit
      a DP load between accesses to static struct fields that span DP boundaries.

      The issue, however, is due to earlier placement on the same data page for
      instances of structs with flexible array members that have incorrectly
      calculated struct size.

      The struct size calculation ignores the size of the flexible array member.

      Example

      Below should be 11 words in size (12 with alignment), however
      the compiler is not including the 7 words from the flexible array:

        typedef struct myStr
        {
             int* next;
             int  id;
             int  size;
             char buffer[];
        } myStr_t, *myStrHandle;
      
        static myStr_t q0 = { .buffer = { [6] = 0 }, };
      

      Here's the generated assembly for above

      	.data
      	.align	2
      	.elfsym	||q0||,SYM_SIZE(4)
      ||q0||:
      	.bits		0,32 ; q0.next @ 0
      	.bits		0,16 ; q0.id @ 32
      	.bits		0,16 ; q0.size @ 48
      	.bits		0,16 ; q0[0] @ 64
      	.bits		0,16 ; q0[1] @ 80
      	.bits		0,16 ; q0[2] @ 96
      	.bits		0,16 ; q0[3] @ 112
      	.bits		0,16 ; q0[4] @ 128
      	.bits		0,16 ; q0[5] @ 144
      	.bits		0,16 ; q0[6] @ 160
      
      	.sblock	".data"
      

      For the submitted test case, the .data section included several of above
      struct instances, followed by several static struct instances for a struct
      type that did not have flexible array members. The DP load insertion
      pass incorrectly misses inserting a DP load for accessing the struct
      at the end of the data page which should have actually been placed on
      the next data page.

      The issue requires below:

      • EABI
      • static structs with flexible array members
      • enough additional static structs on the same data page such that
        the incorrect struct size results in a struct being placed on
        the same data page instead of the next data page

      Here's an example showing a case in C and assembly where s4 is on
      the wrong data page:

      C code
      
      typedef struct myStr
      {
          bq_size_t* next;
          bq_size_t  id;
          bq_size_t  size;
          char       buffer[];
      } myStr_t, *myStrHandle;
      typedef struct LogChannel
      {
          const logDestination_t destination;
          logBufferHandle_t      logBuffer; 
          myStrHandle            readyQueue;
          myStrHandle            doneQueue; 
      } logChannel_t;
      
      static myStr_t q0 = { .buffer = { [6] = 0 }, };
      static myStr_t q1 = { .buffer = { [6] = 0 }, };
      static myStr_t q2 = { .buffer = { [6] = 0 }, };
      
      static logChannel_t s0  = { .readyQueue = &q0, };
      static logChannel_t s1  = { .readyQueue = &q1, };
      static logChannel_t s2  = { .readyQueue = &q2, };
      static logChannel_t s3  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s4  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s5  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s6  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s7  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s8  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s9  = { .logBuffer = ((void *)0x0), };
      

      Assembly

      	.data
      	.align	2
      	.elfsym	||q0||,SYM_SIZE(4)
      ||q0||:
      	.bits		0,32 ; q0.next @ 0
      	.bits		0,16 ; q0.id @ 32
      	.bits		0,16 ; q0.size @ 48
      	.bits		0,16 ; q0[0] @ 64
      	.bits		0,16 ; q0[1] @ 80
      	.bits		0,16 ; q0[2] @ 96
      	.bits		0,16 ; q0[3] @ 112
      	.bits		0,16 ; q0[4] @ 128
      	.bits		0,16 ; q0[5] @ 144
      	.bits		0,16 ; q0[6] @ 160
      
      etc same for q1, q2 as above
      
      NOTE: each of above is actually 11 words though 12 words
      with alignment so above should consume 36 words of the data page
      
      	.data
      	.align	2
      	.elfsym	||s0||,SYM_SIZE(8)
      ||s0||:
      	.bits	0,16       ; s0.destination @ 0
      	.space	16
      	.bits	0,32       ; s0.logBuffer @ 32
      	.bits	||q0||,32  ; s0.readyQueue @ 64
      	.space	32
      
      etc same for s1, s2 as above
      NOTE: using correct size of 12 words for each q0,1,2
      causes below to start at word 60, and halfway through
      below is word 64 which is start of next data page
      
      	.data
      	.align	2
      	.elfsym	||s3||,SYM_SIZE(8)
      ||s3||:
      	.bits		0,16 ; s3.destination @ 0
      	.space	16
      	.bits		0,32 ; s3.logBuffer @ 32
      	.space	64
      

      And with incorrect size 4 set for q0,1,2 the
      code generator incorrectly determines data page
      boundary to be within s7 such that accessing 2nd
      member of s7 requires new DP to be loaded.

      original description:

      The attached C file has these lines ...

      static logChannel_t s3  = { .logBuffer = ((void *)0x0), };
      static logChannel_t s4  = { .logBuffer = ((void *)0x0), };
      /* next 2 lines are inside a function */
          s3.logBuffer = localLogBuffer;
          s4.logBuffer = localLogBuffer;
      

      Build it ...

      % cl2000 -@options.txt file.c
      

      A page boundary at offset 0x40 passes through the middle of s3, The field logBuffer is before the page boundary. This can be seen with these snippets of the assembly listing file ...

           191 0000003c       ||s3||:
           208 00000044       ||s4||:
      

      The assembly code issued for these statements is ...

              MOVL      @||s3||,ACC           ; [CPU_ALU] |1479| 
              MOVL      @||s4||,ACC           ; [CPU_ALU] |1480| 
      

      Because of the page boundary, the DP register should be updated between these two MOVL instructions, but it isn't.

            Assignee:
            TI User
            Reporter:
            TI User
            Votes:
            2 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved:

                Connection: Intermediate to External PROD System
                EXTSYNC-6679 - C28 EABI: missing DP load caused by...
                SYNCHRONIZED
                • Last Sync Date: