This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

a proposal ?

I have a thought that would make many processes much more effective and, being only one voice, I would like to know if there is a demand before I propose it to Keil.

This is the result of me having to change a switch statement to a series of IFs to make an ISR process fast enough.

I realize, and thus do not complain, that Keil caould not implemet an ANSI-C switch more efficient than the way it is done.

'n' below will be either 2 or 3 (if Keil decide to implement, they can choose)

a Proposal:
if #pragma QUICKSWITCH then
a) no case value can be more than 255
b) all case values must be multiples of n
c) the switch will be implemented as a jump table

Erik

  • The compiler actually generates already a jump table if the cases are in sequence.

    Take a look to: http://www.keil.com/support/docs/1316.htm

    There is a quite extensive algorithm that decides whether a jump table or compare operations are more efficient.

  • the wonders of documentation (not Keil in particular) I have yet to find a manual that is really useful.

    One example (again 'typical' not Keil in particular) if you do not remember the OVARLAYABLE statement literally, you will never find it.

    Reinhard, while the link you give is good, would you consider having (or is there) a 'manual' named "helping the compiler"?

    Erik

  • In my opinion, a pragma would still be useful.

    It's nice that we know that the code generator just happens to generate a jump table when labels are in sequence. But that's not necessarily convenient for the programmer, sometimes can generate extra code (when two labels not in order need to do the same thing, for instance), and isn't a documented "contract" between the source and the compiler. Nothing prevents this quirk of the code generator from changing in the next release. And if you have to share code between two compilers, both compilers may not agree on a convention for source layout to indicate code generation choices.

    A pragma, defined in the manual, would provide a way to force the needed behavior in the cases where it's necessary in a stable and consistent manner.

  • isn't a documented "contract" between the source and the compiler.

    Note that since the language itself doesn't provide any way of spelling out such a contract, it can only exist between the source and one given compiler, using some language extension. That would tend to completely invalidate your other point:

    And if you have to share code between two compilers, both compilers may not agree on a convention for source layout to indicate code generation choices.

    Those two compilers won't agree on the meaning of some language extension either.

    Let's face it: the only realistic way to get something like this interpreted the same way by more than one compiler is to feed it into your national standardization organization and keeping your fingers crossed for about 10 years until it possibly comes out the other end as a revised standard.

    A pragma, defined in the manual, would provide a way to force the needed behavior in the cases where it's necessary in a stable and consistent manner.

    It's just as likely to mean nothing, or worse yet, something completely different, to that hypothetical other compiler your colleague will have to port the code to next week. #pragma's (except the couple C99 ones) and portability are mutually contradictive concepts.

  • portability my a..

    The one and only interest in the means of execution of any statement is throughput. If you are throughput critical, then there is no 'portability'.

    Should you use #pragma QUICKSWITCH for any reason other that throughput, it does not matter one iota what 'that other compiler' does with it.

    The very exisience of the verb 'portinng' is proof that portability is a myth. Reuse, template, basis sure - but 'portable' spare me.

    Yes, where there is no timing concerns and memory is ample you may have a case for 'portability' but from a '51 to a HC08 come on!

    Erik

  • "If you are throughput critical, ..."

    then write it in assembly language.

  • "If you are throughput critical, ..."

    then write it in assembly language.


    I do, but if there is a way I can do it ib C that would still be preferable.

    The statement "then write it in assembly language" sure does kill the argument re portability :)

    Have a great weekend

    Erik

  • Those two compilers won't agree on the meaning of some language extension either.

    True, but it does at least give you a common place to put the toolchain-specific notes. #pragma itself is standard. So, you'd wind up with

    #if __C51__
    #pragma Keils_pragma_for_jumptable
    #endif
    #if __GCC___
    #pragma GCCs_pragma_for_jump_table
    #endif

    // common code for the actual "C" part.

    If you really hate #ifs in your code, you could make that series a single file so that the change in the actual .c file reduces to

    #include "pragma_jumptable"

    The pragma also documents the requirement that this particular switch must be a jump table much more clearly than relying on a source formatting convention. (Might as well say all function declarations have to start in column 8, while all statements have to start in column 12.)

    As for using the C source proper, consider the worst case -- one compiler decides that in order, ascending values secretly means "jump table", while the other compiler decides it means if-then-else. You're exactly right that this contract is one per compiler, which is exactly why it can't be in the source itself. That source has to be fed to all of the compilers. You can't both order the switch labels ascending and order them descending at the same time.

    So, the magic signal to convey these sorts of wishes has to be something other than the source itself. That's what #pragma was invented to do.

    Standardizing the pragmas themselves would be nice as well. Pragmas also could use a per-vendor "namespace". Lots of compilers, for example, support "#pragma pack", but they don't all mean the same thing by it. Compilers can ignore #pragmas they don't recognize, but there's not a good way to make sure they don't misinterpret a pragma they do recognize, but with different semantics.

  • this seems to 'follow the rules' but there still is the slow switch

     348          ////////////////////////////////////////////////////////////
     349          //
     350          //  ISR 14: void ISR_T3 (void)
     351          //
     352
     353          void ISR_T3 (void) interrupt 14 using 1
     354          {
     355   1        SB1_TMR3CN_TR = 0;
     356   1        SB1_TMR3CN_TF = 0;
     357   1        switch (GC4holdState)
     358   1        {
     359   2        case G4HS_IDLE: // 0x00 should never happem
     360   2          catchT3(0x01);
     361   2          break;
     362   2
     363   2        case G4HS_RCDN: // 0x01 receive complted now processing
     364   2          SBG_P0_D485 = 1;
     365   2          SG_SFRPAGE = 0;
     366   2          SB0_SCON0_TB8 = 1;
     367   2          S0_SBUF0 = RSP_BUSY;
     368   2          break;
     369   2
     370   2        case G4HS_RCCM: // 0x02 processing of receive complted
     371   2          SBG_P0_D485 = 1;
     372   2          SG_SFRPAGE = 0;
     373   2          SB0_SCON0_TB8 = 1;
     374   2          S0_SBUF0 = RSP_CMPL;
     375   2          GC4holdState = G4HS_IDLE;
     376   2          break;
     377   2
     378   2        case G4HS_RCER: // 0x03 receive complted, error detected, record ignored
     379   2          SBG_P0_D485 = 1;
     380   2          SG_SFRPAGE = 0;
     381   2          SB0_SCON0_TB8 = 1;
     382   2          S0_SBUF0 = RSP_ERR;
     383   2          GC4holdState = G4HS_IDLE;
     384   2          break;
     385   2
     386   2        case G4HS_RCHO: // 0x04 receive response completed hold off switch to receive
     387   2          SBG_P0_D485 = 0;
     388   2          GC4holdState = G4HS_IDLE;
     389   2          break;
     390   2
     391   2        default:
     392   2          catchT3(0xff);
     393   2          break;
     394   2
     395   2        }
     396   1      }
    
    
                                               ; SOURCE LINE # 353
                                               ; SOURCE LINE # 355
    000B C2CA              CLR     SB1_TMR3CN_TR
                                               ; SOURCE LINE # 356
    000D C2CF              CLR     SB1_TMR3CN_TF
                                               ; SOURCE LINE # 357
    000F E500        E     MOV     A,GC4holdState
    0011 120000      E     LCALL   ?C?CCASE
    0014 0000        R     DW      ?C0048
    0016 00                DB      00H
    0017 0000        R     DW      ?C0049
    0019 01                DB      01H
    001A 0000        R     DW      ?C0050
    001C 02                DB      02H
    001D 0000        R     DW      ?C0051
    001F 03                DB      03H
    0020 0000        R     DW      ?C0052
    0022 04                DB      04H
    0023 0000              DW      00H
    0025 0000        R     DW      ?C0053
    Thus I repeat my call for a #pragna QUICKSWITCH

    Erik

  • Which OPTIMIZE level are you using?

    2, just to get the variable overlaying.


    I do not and will not use optimizers, they defeat the NASA principle "we fly what we test" which, since some testing for unforseen events is done by pegging values in the emulator makes working with 'optimized' code an impossibility.

    doing 'quickswitch' should be a compiler issue, not an optimizer issue.

    Erik

  • Two things to consider:

    - almost everyone uses the default optimization 8. You can still 'fly' what you 'test' since full debug information is provided. Therefore this is also the best tested compiler mode.

    - switch/case optimzation requires some other information that is generated by optimizers that need to run. switch/case optimzation is enabled with OT(4) and above.

  • - almost everyone uses the default optimization 8. You can still 'fly' what you 'test' since full debug information is provided.
    there is with any optimization (I do not consider overlaid variables optimization) several disadvantages:
    a) there will be many mismatches between source and actual code
    b) I know the Keil compiler is so excellent that I do not need to check the assembler, but I do anyhow, see also d).
    c) 'merged' sequences makes reasonable breakpointing impossible.
    d) often a fault in the c source is revealed by what is assembled from it
    and .. finally .. e) I am not "almost everyone" and have no desire to be.

    Therefore this is also the best tested compiler mode
    why 'tested' this is not about 'testing' it is functionality.

    switch/case optimzation requires some other information that is generated by optimizers that need to run.
    I am VERY curious, which added information would be required to make my example above into a jump table.

    Erik

  • a footnote to the above:

    I once had a timing error that only did show when the optimizer was active. Debugging it, by any 'helped means' (emulation) was impossible beacuse the critical sections were 'combined' with frequent operations by the optimizer. For that reason it took 6 weeks to find a problem that, had it shown in non-optimized, would have been a matter of a day or two. It turned out to be a subtle bug in a (then) recently released Atmel derivative, not the design.

    Erik

  • void ISR_T3 (void) interrupt 14 using 1
    {
       SB1_TMR3CN_TR = 0;
       SB1_TMR3CN_TF = 0;
       switch (GC4holdState)
       {
         case G4HS_IDLE: // 0x00 should never happem
              catchT3(0x01);
              return;
         case G4HS_RCDN: // 0x01 receive complted now processing
              SBG_P0_D485 = 1;
              SG_SFRPAGE = 0;
              SB0_SCON0_TB8 = 1;
              S0_SBUF0 = RSP_BUSY;
              return;
         case G4HS_RCCM: // 0x02 processing of receive complted
              SBG_P0_D485 = 1;
              SG_SFRPAGE = 0;
              SB0_SCON0_TB8 = 1;
              S0_SBUF0 = RSP_CMPL;
              GC4holdState = G4HS_IDLE;
              return;
         case G4HS_RCER: // 0x03 receive complted, error detected, record ignored
              SBG_P0_D485 = 1;
              SG_SFRPAGE = 0;
              SB0_SCON0_TB8 = 1;
              S0_SBUF0 = RSP_ERR;
              GC4holdState = G4HS_IDLE;
              return;
         case G4HS_RCHO: // 0x04 receive response completed hold off switch to receive
              SBG_P0_D485 = 0;
              GC4holdState = G4HS_IDLE;
              return;
       }
       catchT3(0xff);
    }
    
    Eric, just a thought to avoid the ccase:

    the above code should result in code without calling the cccase. Thats my trick to work around your proposal.

    Thomas