[Megoldva] Kioptimalizálódhat-e a függvényhívás?

Fórumok

Nézzük az alábbit:

#define INTERRUPT_test_and_run(PIE, PIR, MASK, FNC) (((PIE) & (PIR) & (MASK)) ?\
                                                        (FNC, true) : false)

void __interrupt() INTERRUPT_InterruptManager(void) {

    bool ready;

    ready = false;
    ready |= INTERRUPT_test_and_run(PIE3, PIR3, _PIR3_TMR0IF_MASK, tmr0_isr());
    ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1RXIF_MASK, rx_isr());
    ready |= INTERRUPT_test_and_run(PIE4, PIR4, _PIR4_U1TXIF_MASK, tx_isr());
    if (!ready) RESET();                                                        // unhandled interrupt
}

Gondolhatja-e a fordító, hogy a RESET() hívásához szükséges ready változó helyes előállításához szükségtelen a makróban hivatkozott FNC meghívása? Mert ha ezt gondolja, akkor épp az interrupt handlert fogja kispórolni, és sovány vigasz lesz annak a néhány byte-nak a megspórolása. :) Veszélyes-e ebből a szempontból ez a makró?

Szerk.: Nem azt állítom, hogy most rosszul fordítja - még nem próbáltam ki -, hanem az a kérdés, hogy vajon ez egy annyira ügyetlen makró, amitől állandóan ott a fejem felett a pallos, hogy egyszer csak kispórolja a fordító az IT handler hívását, vagy ez így teljesen jó, netán teljesen rossz, vagy picit módosítani kellene rajta?

Megjegyzés:

Annak, aki esetleg érdekesnek gondolja, úgy tűnik, az eredeti makró - itt fentebb - hibás, s a fordító nem érzi szükségesnek a vessző operátor előtti függvény hívását, hiszen a kifejezés anélkül is kiértékelhető. Akkor viszont nem hívódik az interrupt handler.

Hozzászólások

Azt szerintem ki kell számítania, mert különben nem tudná a végén, hogy reset legyen, vagy sem. Itt az a bajom, hogy az (FNC, true) az az FNC hívása nélkül is true, tehát FNC hívása elhagyható a kifejezés eredményének szempontjából, viszont épp az FNC a katarzis ebben a dologban. :)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Igen, vessző operátor. Ezt láttam oda a legalkalmasabbnak. Balról jobbra, és a leggyengébben „köt”.

Szerk.: Csak ugye FNC nélkül is tudható, hogy (FNC, true) az true, tehát nem kell FNC-t hívni. Szóval sajnos ez így nem jó, már látom.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Attól tekintsünk el, hogy a ready változó neve kifejezőbb volna, ha átnevezném done-ra. :)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Részben megvan a válasz:

warning: (759) expression generates no code

Szomorú vagyok, át kell alakítani. Az előbb nem vettem észre, csak most. :(

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Egyre kevésbé értem. Ha az a ready volatile, akkor nem ad a fordító warningot azzal, hogy a kifejezésből nem fordult kód, viszont byte-ra pontosan ugyanolyan hosszúságú kód fordul. Másfelől, ha nem is hívja FNC-t, a kifejezést szerintem nem spórolhatja le, ellenben honnan tudhatná, hogy a végén kell-e a RESET() hívása, vagy sem? Azt elhiszem, hogy FNC hívást kispórolja, de a kifejezést muszáj lenne kiértékelnie.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Imigy?

#define INTERRUPT_test_and_run(ready,C1,C2,C3,funct) \
    do                          \
     {  if ( (C1)&(C2)&(C3) )   \
         {   funct();           \
             ready |= true;     \
         }                      \
     } while(0)

void __interrupt() INTERRUPT_InterruptManager(void) 
{
 bool ready;

 ready = false;
 INTERRUPT_test_and_run(ready, PIE3, PIR3, _PIR3_TMR0IF_MASK, tmr0_isr);
 INTERRUPT_test_and_run(ready, PIE4, PIR4, _PIR4_U1RXIF_MASK, rx_isr);
 INTERRUPT_test_and_run(ready, PIE4, PIR4, _PIR4_U1TXIF_MASK, tx_isr);
 if (!ready) RESET();        // unhandled interrupt
}

Elengedtem a makrót, végül ezt csináltam:

typedef void (*isr_t)(void);

static inline bool interrupt_test_and_run(uint8_t pie, uint8_t pir, uint8_t mask, isr_t isr) {

    bool ret;

    ret = isr && (pie & pir & mask);
    if (ret) isr();
    return ret;
}

void __interrupt() INTERRUPT_InterruptManager(void) {

    bool done;

    done = false;
    done |= interrupt_test_and_run(PIE3, PIR3, _PIR3_TMR0IF_MASK, tmr0_isr);
    done |= interrupt_test_and_run(PIE4, PIR4, _PIR4_U1RXIF_MASK, rx_isr);
    done |= interrupt_test_and_run(PIE4, PIR4, _PIR4_U1TXIF_MASK, tx_isr);
    if (!done) RESET();                                                         // unhandled interrupt
}

Kérdés, hogy a függvénypointer átadásánál rájön-e arra, hogy ez fordítási időben eldől, mert már akkor tudható a cím, és közvetlenül egy hívássá fordítható mindenféle futásidejű címszámítás nélkül.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Igen, néha szoktam beleírni efféle védelmeket. NULL ellen van. :) Már csak azért is, mert sok függvényemet írom meg úgy, hogy ha kérek tőle valamit, akkor átadom a változó címét, ha nem, akkor NULL-t adok meg neki, persze ettől nem borul fel:

int8_t valami(uint16_t *var) {

if (var) *var = kifejezés;
return 0;
}

uint16_t result;

valami(NULL);

vagy

valami(&result);
 

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE