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.

Linux/MSP430FR2422: MSP430-GCC 6.1.0.0 optimization compiler error: interrupt handler calls another interrupt handler using normal calling convention

Part Number: MSP430FR2422

Tool/software: Linux

GCC erroneously optimizes a call from one interrupt function to another, using standard calling convention.

This results in an eventual stack overflow, as the interrupt returns using reti, not ret.

In this particular example, fn_8 and fn_11 are separate functions, but the compiler slips a call() into the second in a hapless attempt to optimize

This is a pretty bad compiler bug.

I was able to work around it by swapping the order of the calls in the fn_08, but that is a hack workaround.

Source:

__attribute((interrupt))
void fn_11(){
TX_INV_MARK += u.tx.offset_fix;
TX_BIT_DOWN_IRQ = u.tx.bit_down_irq;
}



__attribute((interrupt))
void fn_08(){
TX_INV_MARK += u.tx.offset_fix;
TX_BIT_DOWN_IRQ = u.tx.bit_down_irq;
}

Assembler (call is bolded):

__attribute((interrupt))
void fn_11(){
e910: 0c 15 pushm #1, r12 ;16-bit words

0000e912 <.LCFI12>:
TX_INV_MARK += u.tx.offset_fix;
e912: 3c 40 68 21 mov #8552, r12 ;#0x2168
e916: 92 5c 08 00 add 8(r12), &0x0392 ;
e91a: 92 03

0000e91c <.Loc.382.3>:
TX_BIT_DOWN_IRQ = u.tx.bit_down_irq;
e91c: 92 4c 02 00 mov 2(r12), &0x200c ;
e920: 0c 20

0000e922 <.Loc.383.3>:
}

0000ea54 <fn_08>:
ea54: bf 15 pushm #12, r15 ;16-bit words

0000ea56 <.LCFI13>:
ea56: b0 12 10 e9 call #-5872 ;#0xe910
ea5a: b4 17 popm #12, r15 ;16-bit words
ea5c: 00 13 reti

  • I was able to replicate that but when I specified the vector used::

    __attribute__((interrupt(TIMER0_A0_VECTOR))) void fn_11()

    The problem vanished.

  • Thanks David, but this is still a problem for me.
    I intentionally do not specify the vector.
    I remap my interrupts at run time via a branch instruction in flash referencing a RAM pointer.

    Any in any case, the compiler should still not produce such code.
  • I am having trouble thinking of a reason to redirect interrupt service via a RAM table when the vectors are in FRAM.
  • I keep my program FRAM locked, and unlocking/locking in an IRQ context would create extra latency.
  • Why would you change the interrupt vectors while servicing an interrupt? I can't imagine that.

    In any case, if things are so dynamic that you can't tell what code you want to execute until the interrupt happens, have the ISR call the appropriate code via a pointer to a function.

  • Of course, because the point is that you determine what function the next interrupt will call.
  • Hi Dave,
    it is of course your decision, but at least based on the limited information and my understanding of it, I'd recommend a different solution to this, than manipulating the code memory independent of whether this is an interrupt vector or non-interrupt code memory. Doing so, is something I would not call good programming practice.
    What about defining and using a state variable, which is being used in the interrupt case to determine which functions to be called? When you manipulate the interrupt vectors, you would need to disable and enable interrupts for the update otherwise you could risk fatal issues. I think in the end you will not have any response time advantages going the path you've entered. I'd rather spend some time on generating the most effective code instead.

    Best regards
    Peter
  • Peter,
    I don't think you are understanding my approach.
    The code at the interrupt vector itself does not change.
    I am NOT manipulating the vector itself, as David Schultz had proposed.

    The instruction at the vector is merely a branch whose destination address changes based on a RAM variable.
    There is no difference between this and the state variable you are proposing, except this solution does not require a costly switch or if statement.
    The RAM variable simply stores the next function to call on the next interrupt.
    And when you look at the assembler, you will see that this is the most effective approach.
    This is established practice for me on the G2553 with an older compiler.

     It's just that the newer compiler for the FR2422 is generating erroneous code, which should be addressed by Mitto.

    -Dave

  • Hi Dave,
    I understand the point with the Compiler in the sense of having trouble with the optimization results...
    What I have trouble with to see the reflection of what you're describing in your last post and and the data you provided before.
    Maybe it's easier, when approaching it from a different side.
    As soon as an interrupt occurs, the built in interrupt logic loads the CPU, after finishing the active instruction, with the interrupt vector of the interrupt table for the occurred interrupt. There's nothing you can change about that. So if I understand you right, you're placing a vector which points to a RAM location, which's content than would decide where to branch further in the next step?
    So as you seem to be firm also with the assembler, what about trying to resolve this by coding this portion of code in assembler?
    If I still should have misunderstood the approach, please let me know.

    Best regards
    Peter
  • Peter, I think you are beginning to understand the approach and the actual interrupt vector code is written in assembler.
    However, most of this thread has been focused on what I am doing and has been completing dodging the fact there is an optimization bug.
    I will be happy to educate you more about my approach if you really want, but it completely misses the original point of this post.

    Again, to put the bug simply, functions that end in IRET should never be called with the normal CALL assembler instruction, end of story.
    In the end, you can either inform the GCC team to fix this or not.
  • Hi Dave,
    understood. Correct the RETI/RET is portion of the issue. We will definitely report this erroneous behavior to the Compiler guys, that's for sure. But as I cannot make commitments on if and when they will be able to fix this, I was just trying to see if there is some other possibility of solving the issues other than fixing the Compiler.

    Best regards
    Peter
  • Okay Peter, thank you for passing on the report. The issue is the compiler bug, and until it is fixed there is no systematic resolution for it other than to detect when it happens and work within that circumstance. The issue will remain open until then, and thank you for your support.
  • Dave,
    I am sorry I can't be of more help. Are you ok with closing this thread for now, as we probably cannot keep it open until the update of the Compiler happens? We will contact you, once the Compiler fix is done, or you can create a new one referring to this original one, in case you'd like to?

    Best regards
    Peter
  • Hi Dave,

    Thanks for reporting this bug, we will aim to fix this in a release later in the year.

    The specific optimization that causes this behaviour is "Identical Code Folding". You can turn this off for functions by passing "-fno-ipa-icf-functions" on the command line.

    I tried to see if you could turn this optimization off for individual functions, using the "optimize" attribute (i.e. __attribute__((interrupt,optimize("no-ipa-icf-functions"))). Unfortunately this has no effect. It appears the optimization can only be turned off per source file. 

  • Thank you Josef, the compiler switch worked well for me and I didn't have to modify my source. Thank you for providing a direct answer and not questioning my programming practice.
  • Hi Dave,

    Since the way you make use of interrupt functions is quite uncommon, I wanted to get your opinion on how we may extend the optimization of identical interrupt functions.

    If your two identical interrupt functions were folded by creating an alias (instead of by creating a wrapper, which is what is happening at the moment), might that cause any problems for your program?

    Using the code snippet from your original post as an example, the proposed behaviour would be roughly equivalent to this:

    __attribute((interrupt)) 
    void fn_11(){ 
     TX_INV_MARK += u.tx.offset_fix; 
     TX_BIT_DOWN_IRQ = u.tx.bit_down_irq; 
    } 
    
    __attribute((interrupt,alias("fn_11"))) 
    void fn_08(); 

    So GCC would detect that the interrupt functions are equivalent and alias them, so they would have the same address. 

    Does your program require these interrupt functions with identical contents to have different addresses? Would this proposal cause any issues?

    Thanks.

    P.S. In the next release we have simply disabled "identical code folding" for interrupt functions, but we are continuing to investigate this possibility of aliasing them for a future release.

  • Josef,

      Thanks for reaching out to me.

      This particular code I wrote would not break on this aliasing technique.

      Let's say fn_11() and fn_8() are part of a background interrupt driven process P, but in separate control branches. Let's say I need another process Q to know the state of P. Then I would expect fn_11 and fn_8 to have different addresses. That way Q would know what branch P was in.

      The expectation that the addresses are different comes naturally since most functions are different. Thus it becomes surprising that different functions, even with identical contents, would have the same address. A similar context for this situation actually exists with aliasing constant values too. You may maintain a pointer to a constant value and you may be concerned more with which constant you are pointing to as well as the actual value.

  • Hi Dave,

    Thanks for providing some further information.

    My thoughts are that we cannot safely alias interrupt functions. As you say, the user would expect different interrupts to have different addresses, and there is nothing in the MSP430 ABI to stipulate that interrupt functions with global visibility could be manipulated in this way.

    Aliasing could possibly work for interrupt functions that are declared static, but I don't think this is something worth pursuing at this time.

    Regards,

**Attention** This is a public forum