Ok, par exemple j'écris :
uint32_t shift15( uint32_t x )
{
return x >> 15;
}
uint16_t mulfractu16u16( uint16_t x, uint16_t y )
{
return (uint16_t)( ((uint32_t)x * (uint32_t)y) >> 16 );
}
voici le code généré pour shift15() (les variables sont tout ce qu'il y a de plus standard, pas register, pas volatile, rien) :
15f0: 8d 81 ldd r24, Y+5 ; 0x05
15f2: 9e 81 ldd r25, Y+6 ; 0x06
15f4: af 81 ldd r26, Y+7 ; 0x07
15f6: b8 85 ldd r27, Y+8 ; 0x08
15f8: 3f e0 ldi r19, 0x0F ; 15
15fa: b6 95 lsr r27
15fc: a7 95 ror r26
15fe: 97 95 ror r25
1600: 87 95 ror r24
1602: 3a 95 dec r19
1604: d1 f7 brne .-12 ; 0x15fa <loop+0xac>
1606: 89 83 std Y+1, r24 ; 0x01
1608: 9a 83 std Y+2, r25 ; 0x02
160a: ab 83 std Y+3, r26 ; 0x03
160c: bc 83 std Y+4, r27 ; 0x04
et voici le code généré pour mulfractu16u16()
160e: 61 96 adiw r28, 0x11 ; 17
1610: ae ad ldd r26, Y+62 ; 0x3e
1612: bf ad ldd r27, Y+63 ; 0x3f
1614: 61 97 sbiw r28, 0x11 ; 17
1616: bd 01 movw r22, r26
1618: 80 e0 ldi r24, 0x00 ; 0
161a: 90 e0 ldi r25, 0x00 ; 0
161c: 63 96 adiw r28, 0x13 ; 19
161e: ee ad ldd r30, Y+62 ; 0x3e
1620: ff ad ldd r31, Y+63 ; 0x3f
1622: 63 97 sbiw r28, 0x13 ; 19
1624: 9f 01 movw r18, r30
1626: 40 e0 ldi r20, 0x00 ; 0
1628: 50 e0 ldi r21, 0x00 ; 0
162a: 0e 94 89 2a call 0x5512 ; 0x5512 <__mulsi3>
162e: bc 01 movw r22, r24
1630: 88 27 eor r24, r24
1632: 99 27 eor r25, r25
1634: 80 e0 ldi r24, 0x00 ; 0
1636: 90 e0 ldi r25, 0x00 ; 0
1638: 69 87 std Y+9, r22 ; 0x09
163a: 7a 87 std Y+10, r23 ; 0x0a
163c: 8b 87 std Y+11, r24 ; 0x0b
163e: 9c 87 std Y+12, r25 ; 0x0c
0our shift15, je ne trouve pas que décaler de 15 fois à droite en décalant 15 fois à droite soit tout à fait pertinent (j'aurais bien vu un seul décalage à gauche, mais bon...)
Pour la multiplication fractionnelle c'est idem. Le compilateur ne comprend pas que caster deux nombres de 16 bits en 32bits avant une multiplication indique qu'il n'a pas besoin de faire une multiplication 32x32, mais qu'il peut faire une multiplication 32=16x16.
J'ai d'autres exemples du même tonneau qui montre que les opérations de bas niveau sont très mal codées (du moins avec le niveau d'optimisation dans lequel je suis).
Je voulais réécrire les fonctions trigonométriques de base (atn, sin) pour les remplacer par des fonctions utilisant des entiers. J'ai été assez étonné car je n'arrivais à un gain de vitesse que dans un rapport de 2 ou 3 par rapport au flottant alors que je m'attendais à un rapport au moins de 5 à 10 (si je me fie à d'autres implémentations). Toutes les routines d'optimisation en entier font appel à des opérations de scaling et à des multiplications dont les résultats sont souvent de type différent des types des opérandes. Si les opérations de bases sont mal codées, ces optimisations sont très difficiles à réaliser.
Optimiser le code en faisant des "procedural abstraction" c'est bien mais si x>>15 est codé aussi mal, ça ne sert pas à grand chose... enfin, c'est ce que je pense...
D'où mon désarrois.
Et donc pour en revenir à la question de base, est-il possible de changer quelque chose, dans l'IDE Arduino (ou à coté) pour améliorer les optimisations ?
Pour haifger, je ne suis pas très clair, je m'en excuse. "backend" ou "frontend" dans un compilateur, c'est souvent là ou le code (presque) final est effectivement généré (presque parce qu'il y a surement des passes supplémentaires pour virer les trucs qui ne servent à rien).