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.

C2000 C/C++ CODE GENERATION TOOLS: COMPILER BUG?

Hi all,

I am using C2000 C/C++ CODE GENERATION TOOLS 5.2.1 with  TMS320F28x DSP family and have been struggling for weeks in trying to understand the reason of a very strange behaviour.

Basically, after recent investigations I am convinced it is a compiler bug.

The bug may be described as follows: C-code generates assembly instructions (like IMPYL  P,XT,XAR6) which use the P register; the most of the times compiler inserts an SPM #0 just before calling them but I noticed that at least in one case it doesn't, resulting in unpredictable results.

The funny thing is that if I force an asm(" NOP"); in my C code, compiler adds an SPM #0 also in this case (and magically everything works fine)!!!

Attached a python file including compiler flags.

I mix C-code with assembly routines. Some of the C-files get automatically generated by MATLAB (so a quite complex scenario)... But, I repeat, just a NOP seems to fix all our problems. It sounds like a compiler bug to me. Is there any patches?

It is a urgent matter as our production has stopped waiting for a fix.

Could anyone please help me out?

Many thanks in advance.

Paolo Benini

  • You ought to try the latest v5.2.x compiler, which is v5.2.11 right now.  Please go here for more detail.

    If that doesn't work out, then we need a test case.  See the last part of the forum welcome message for details.  In this particular case, I think all we need is the function where you add the asm(" nop") statement.

    Thanks and regards,

    -George

  • Sorry George but according to the defect history, this issue seems neither to be known nor to be fixed.

    I wouldn't waste my time in trying something unless it is really worthwhile.

    Regards,

    Paolo

  • Anyhow, I have tried with version 5.2.11 and it definitely does NOT fix the problem.

    Regards,

    Paolo

  • The TI C2000 C calling convention requires that PM=0 upon entry to the function; the compiler does not emit code to change the value of PM because it knows PM should be 0 already.  This is not a bug.

    Most likely, the user has some hand-coded assembly code (I suspect an interrupt function) which sets PM to a non-zero value and doesn't set it back before making a function call or returning.

  • First of all: which calling convention are you talking about? This is not documented at all. Secondly, no interrupt function has been written in assembly; as far as C-interrupt routines are concerned, entering an interupt routine causes the full context to be saved. I literally quote from the C-compiler manual:

    "Functions that handle interrupts follow special register-saving rules and a special return sequence. The
    implementation stresses safety. The interrupt routine does not assume that the C run-time conventions for
    the various CPU register and status bits are in effect; instead, it re-establishes any values assumed by the
    run-time environment. When C/C++ code is interrupted, the interrupt routine must preserve the contents of
    all machine registers that are used by the routine or by any function called by the routine. When you use
    the interrupt keyword with the definition of the function, the compiler generates register saves based on
    the rules for interrupt functions and the special return sequence for interrupts
    ."

    All our hand-coded assembly functions PUSH and POP both ST0 and ST1. As you might know PM bits are part of ST0 register.

    It sounds like a compiler bug to me. Another clue that makes me believe it: there are other parts of the compiler generated assembly code where SPM #0 has been automatically inserted before instructions which use the P register (or instruction which may be affected by PM bits), if no shift is needed.

    Paolo Benini

  • This issue is also being discussed on our internal forum which is adding to the confusion. In our internal discussion the code was given to us as:

    void TEST(void){   

        //asm(" NOP");   // if i  comment it out, compiler doesn't insert an SPM #0

        TT.Test_0 = _IQ16mpy(TT.Test_1,TT.Test_2);

    }


    In this case, the calling convention is that any caller to 'TEST' must ensure PM=0 before the call. In this simple case there is no bug. I'm guessing that the code you have is not just a simple routine with a single _IQ16mpy intrinsic though. The compiler does track the value of the PM bits through a routine, inserting SPM instructions when necessary. We'll have to get a way then to reproduce the actual problem, to see why an SPM #0 would be missing.

  • I don't know actually what code has been given to you, I sent much more than that to Peter Liu [FAE, Embedded Processors(MCU & DSP) - Texas Instruments Taiwan].

    Of course I couldn't send the whole application for obvious reasons (intellectual property protection), but a complete C-module containing the mentioned functions which should be enough to reproduce and investigate the problem (in my opinion).

    Please let me know if you need something more.

    Nevertheless, I still don't understand why you guys keep on talking about "calling conventions": there is no assembly routine calling the "TEST" function and so the compiler itself must respect its own convenctions. If a C-function calls our "TEST" function, the compiler must ensure PM=0 before calling!!!

    I repeat, we are available to discuss the possibility to send you other code if absolutely necessary but using the whole environment I sent to Peter I'm pretty sure you would be able to reproduce the problem (as Peter and I have been able to).

    Paolo Benini

  • Yes, Peter has told us he can provide a better example. And we agree with your analysis. If only C functions are calling TEST, the compiler must make sure PM=0. Right now we don't have any cases where the compiler is not doing that. We were thinking there was possibly a hand-coded assembly routine calling TEST not setting PM=0. If no hand-coded assembly is involved then we have to look at all the calls to this routine. The bug might be not that there is a missing SPM #0 just before the intrinsic, but that its missing at the call into this routine.

  • Paolo Benini said:

    First of all: which calling convention are you talking about? This is not documented at all.

    The "TMS320C28x Optimizing C/C++ Compiler v6.0" (SPRU514D), Section 7.2.2 "Status Registers" table 7-4 "Status Register Fields" states that PM is required to be 0 upon entry to a C-callable function.

    Paolo Benini said:

    Secondly, no interrupt function has been written in assembly; as far as C-interrupt routines are concerned, entering an interupt routine causes the full context to be saved.

    I mention interrupt functions because sometimes control flow is not very clear when interrupts are involved, and often such problems are found there.  For instance, if the function TEST is called (possibly indirectly) by an interrupt function which sets PM to some non-zero value, it's not enough to save and restore PM at the interrupt function entry and exit; it must also be set to 0 before calling any C-callable function.  However, since you've said that no interrupt function has been written in assembly, we can rule that out.  As Tom suggests, we'll need an expanded test case to find where PM is being set and not cleared before TEST is called.  The key thing is to find where PM is set to a non-zero value.

  • Paolo Benini said:

    [..]  the compiler itself must respect its own convenctions. If a C-function calls our "TEST" function, the compiler must ensure PM=0 before calling!!!

    Absolutely, and if it is not, that is a bug.

    However, I haven't seen any code but the stub function TEST, which does not reproduce the bug.  I can't do anything without an expanded test case which shows where PM is being set to a non-zero value and not reset before calling a C-callable function.

  • Attn. ThomasS,

    Attached you'll find a text file containing all the "C" and assembly functions involved in the call chain.

    Of course you won't be able to build it; its only purpose is to provide you with a better picture of the whole thing.

    Hope this helps.

    Should you need something else, please let me know.

    Many Thanks

     

  • Thank you. The issue is in our bug tracking database as SDSCM00040712. You can track the issue using the SDOWP link in my signature.

  • Happy to hear that.

    Do you know when it will be fixed?

    This issue is compromising our production as we cannot rely on what we're developing.

  • I was debugging the issue today with the code supplied by Peter Liu. However, I don't have enough to know the cause of the problem. For the routine that has the IMPYL instruction that needs a SPM #0, I don't have any way of tracing the value of the PM bits through the routine. The PM bits should be zero upon entrance into the routine. And on return from the function calls within that routine the PM bits should also be zero. I'm guessing the other IMPYL instructions in this routine are ok. Something occurs in the middle of the routine that sets the PM bits that the compiler misses perhaps. (I don't see anything on visual inspection.) Do you know the location in that routine where the PM bits are no longer 0?

  • Thomas, I imagine you mean the "_IQ16mpy()" routine, right? Well, nothing happens in the middle of the routine. Something happens outside and before. When the routine begins PM bits have already been changed. In my understanding, the compiler should always put a SPM #0 before calling a function which is using such instructions, unless PM bits have already the desired value (only compiler knows as it translates C-code into assembly). It seems to me that the compiler doesn't realise (or doesn't remember) PM bits have previously been changed somewhere and so it doesn't believe SPM #0 instruction is really necessary...

    How can I help you in investigating this issue? Try to explain me what you would need and we'll see what we can provide you with.

  • We need the source code for the function which sets PM to a non-zero value and either returns or calls another function before setting PM back to zero, as it should.

    Which function sets PM to a non-zero value?

  • The only functions which set PM bits to a non-zero value are the assembly ones included in the attached Calling_Chain.c.

    All our assembly functions PUSH and POP ST0. This obviously means they cannot be blamed at all: whatever they do inside, initial status (as far as PM bits is concerned) is always restored before returning.

  • The code in question looks something like this:

            LCR       #_an_asm_function ; [CPU_] |file.c:369|
            MOVW      DP,#_b_sym ; [CPU_U]
            MOVL      XAR4,@_b_sym ; [CPU_] |file.c:374|
            MOVB      XAR0,#152             ; [CPU_] |file.c:374|
            SETC      SXM                   ; [CPU_]
            MOVL      XAR5,@_b_sym ; [CPU_] |file.c:379|
            MOV       AL,*+XAR4[AR0]        ; [CPU_] |file.c:374|
            MOVB      XAR0,#40              ; [CPU_] |file.c:379|
            ASR       AL,2                  ; [CPU_] |file.c:374|
            MOVW      DP,#_a_sym ; [CPU_U]
            MOVL      XT,*+XAR2[AR0]        ; [CPU_] |file.c:379|
            MOVB      XAR0,#80              ; [CPU_] |file.c:379|
            MOV       ACC,AL                ; [CPU_] |file.c:374|
            MOVL      XAR6,ACC              ; [CPU_] |file.c:374|
            IMPYL     P,XT,XAR6             ; [CPU_] |file.c:379|

    The compiler tracks the possible value of the PM bits through each instruction. In the case above, upon entrance into this entire routine the PM bits must be 0. There are no instructions that will change the PM bits throughout the routine except for the calls. But if a call does alter the PM bits it must set execute a SPM  #0 before the return. Therefore at the IMPYL there is no need to generate a SPM #0 since it should be 0 at that point.

    One scenario that could go wrong is an assembly routine with a push and pop of STO but also changes the PM bits in the middle of the routine. That won't work since the compiler is relying on PM being zero after the call. 

    (No, the previous paragraph isn't correct is it. Any chance a routine is missing a push or pop? Any chance of an instruction changing the PM bits after a pop?)

    Another scenario is if one of the called C routines contains generated code that changes the PM bits but is modeled incorrectly such that the compiler does not know the PM bits have been changed. That would also cause this bug. 

    So the bug isn't in this code, but in one of the called functions.

  • So what are you expecting from me? Be clearer, please. Do you need all the functions called before?

    There are some inline C-functions automatically generated by MATLAB, as well.

    If you need some specific function you're suspecting, please tell me and I will send them through.

  • The bug isn't in the code that we have. It is in one of the calls made from this routine. There is a routine somewhere setting the PM bits but not clearing them before a return.  If we have all the code we may be able to find the bug. Are you able to run a trace through the routine and track the PM bits? Is it possible to cut down the code and make a smaller test case? That could help lower the amount of code to examine.

  • No I'm not. My JTAG emulator does not have TRACE capabilities.

    Not sure we can send you the whole application and even so, you would need the hardware in order to run it.

    A first simple question: is there any assembly instruction which might indirectly change PM bits (except SPM)? Second question: is there any way to trace PM bits other than using emulator TRACE?

    Thanks for the help.

  • Hi everybody,

    Eventually I found the problem: it's definitely NOT a compiler bug but anyway a very tricky and nasty one!

    Briefly: inside an assembly routine, after a PUSH ST0 (16-bit register) there was a PUSH XT (32-bit register). Unfortunately, the latter requires the stack to be even-aligned; if not, it would overwite the previous location (ST0) to impose even alignment in case the SP is odd. This behaviour caused ST0 not to be correctly restored with the POP. In particular, PM bits inside ST0 affected the implicit function "IMPYL     P,XT,XAR6" (which relies on PM bits for shift operations).

    I would like to thank everybody for the availability and the support, although I've been finally able to discover and then fix the bug by myself.

    I have some more questions about my fix but I will start a new discussion in a more appropriate forum.