Forum: Compiler & IDEs GCC-Interna: Optimierung von Bitmasken


von Sven P. (Gast)


Lesenswert?

Hallo,

wieder so eine Sache, die sich mir nicht erschließt:
MODULATE_A ist hier ein Bitfeld von einem Bit Breite, das auf ein SFR 
außerhalb des I/O-Bereiches zeigt (hier TIMSK, Details im Anhang).

Dann nehme man folgende Schnippsel her:
1
/* Variante A */
2
if (phase_a > 0)
3
  MODULATE_A = 1;
4
else
5
  MODULATE_A = 0;
und
1
/* Variante B */
2
MODULATE_A = (phase_a > 0);

Da fällt auf (avr-gcc 4.5.1, -Os): Bei Variante A werden schlicht und 
ergreifend zwei Masken erzeugt und mit ori und andi verarbeitet:
1
 520:  49 b7         in  r20, 0x39  ; 57
2
 522:  88 23         and  r24, r24
3
 524:  11 f0         breq  .+4        ; 0x52a <__stack+0xcb>
4
 526:  48 60         ori  r20, 0x08  ; 8
5
 528:  01 c0         rjmp  .+2        ; 0x52c <__stack+0xcd>
6
 52a:  47 7f         andi  r20, 0xF7  ; 247
7
 52c:  49 bf         out  0x39, r20  ; 57

Wohingegen der GCC sich bei zweiter Variante mit obskuren Operationen 
abquält:
1
 4fe:  41 e0         ldi  r20, 0x01  ; 1
2
 500:  88 23         and  r24, r24
3
 502:  09 f4         brne  .+2        ; 0x506 <__stack+0xa7>
4
 504:  40 e0         ldi  r20, 0x00  ; 0
5
 506:  42 95         swap  r20
6
 508:  40 7f         andi  r20, 0xF0  ; 240
7
 50a:  89 b7         in  r24, 0x39  ; 57
8
 50c:  8f 7e         andi  r24, 0xEF  ; 239
9
 50e:  84 2b         or  r24, r20
10
 510:  89 bf         out  0x39, r24  ; 57

Woher kommt das denn? Je nachdem, welches Bit gemeint ist (hier OCIE1A) 
schiebt und rotiert und addiert er sich einen zurecht und macht 
eigentlich alles, nur nicht das schicke, schlanke ori/andi von zuvor.

Danke und Gruß,
Sven

von Sven P. (Gast)


Angehängte Dateien:

Lesenswert?

Ja, die Sache mit dem Anhang...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wie wär's mit einem compilierbaren Beispiel?
Welche Optionen sind gesetzt?
Was ist phase_a?
Wieso IN und OUT wenn das auf ein "SFR außerhalb des I/O-Bereiches 
zeigt"?
Welcher µC?

Übersetze mit -fdump-tree-all -da (erzeugt ca. 150 temporäre Dateien je 
nach Verschalterung) und du siehst zumindest den Pass wo es passert 
(bzw. nicht passiert).

AFAIK implementiert das avr-Backend keine Pattern für Vergleiche, also 
keine Pattern "scond" wie in den Internals [1] beschrieben

[1] 
http://gcc.gnu.org/onlinedocs/gccint/Standard-Names.html#Standard-Names

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. schrieb:

> "scond"

Die heissen inzwischen cstoremode4

von Sven P. (Gast)


Angehängte Dateien:

Lesenswert?

Johann L. schrieb:
> Wie wär's mit einem compilierbaren Beispiel?
Auha, sorry. Ist wieder bei der Vorschau abhanden gekommen :-(

> Welche Optionen sind gesetzt?
Übersetzung:
1
$ avr-gcc -Wall -Wextra -mmcu=atmega8 -Os -g -DF_CPU=16000000UL --std=c99 -ffunction-sections -fdata-sections -o avrtest.o -c avrtest.c

Bindung:
1
$ avr-gcc -mmcu=atmega8 -g -Wl,--gc-sections -o object avrtest.o

Listing:
1
$ avr-objdump -h -S -d object > list

Version:
1
$ avr-gcc --version
2
avr-gcc (GCC) 4.5.1




> Wieso IN und OUT wenn das auf ein "SFR außerhalb des I/O-Bereiches
> zeigt"?
Korrigiere, außerhalb des bit-adressierbaren I/O-Bereiches. Das sollte 
zum Ausdruck bringen, dass sbi/cbi nicht mehr funktionieren.

> Übersetze mit -fdump-tree-all -da (erzeugt ca. 150 temporäre Dateien je
> nach Verschalterung) und du siehst zumindest den Pass wo es passert
> (bzw. nicht passiert).
>
> AFAIK implementiert das avr-Backend keine Pattern für Vergleiche, also
> keine Pattern "scond" wie in den Internals [1] beschrieben
Tatsächlich, interessant, mal hineinzuschauen.
Bereits im dritten Schritt (gimple) zerlegt er die erste Variante in 
einen bedingten Sprung mit ori/andi, wohingegen sich die zweite Variante 
bis zum Schluss als Ausdruck hält und dann wohl mehr oder weniger 1:1 
übersetzt wird.

Danke!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven P. schrieb:
> Übersetzung:
> ...

Bei solchen Beispielen reicht schon compilierbar im engeren Sinne, also
1
$ avr-gcc -c foo.c -save-temps ...

Mit save-temps werden s- und i-File nicht gelöscht. Linkfähig (main 
etc.) braucht es nicht zu sein.

avr-gcc 4.5 übersetzt die Beispiele
1
/* Variante A */
2
void foo_a (uint8_t phase_a)
3
{
4
    if (phase_a > 0)
5
        MODULATE_A = 1;
6
    else
7
        MODULATE_A = 0;
8
}
9
10
/* Variante B */
11
void foo_b (uint8_t phase_a)
12
{
13
    MODULATE_A = (phase_a > 0);
14
}

so wie hingeschrieben. In B berechnet er zunächst phase_a != 0 (phase_a 
ist unsigned), das ist 0 oder 1, und schiebt das dann an die benötigte 
Bitposition 4 (swap+andi).

Das Problem bei der Optimierung ist, daß man bei der Erzeugung des Bits 
schon was über dessen Verwendung wissen müsste (bzw. umgekehrt), um das 
zur Optimierung zu verwenden. So wäre es in Fall B günstiger, das Bit 
nicht in Position 0 zu erzeugen, sondern in Position 4. Die optimale 
Sequenz
1
in   r20, 0x39
2
cbr  r20, 4
3
cpse r24, __zero_reg__  ;; phase_a
4
sbr  r20, 4
5
out  0x39, r20
mit gcc zu erzeugen wäre recht viel Arbeit und würe auch Codeänderungen 
an Stellen nach sich ziehen, die scheinbar nichts mit der genannten 
Sequenz zu tun haben.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.