GCC-Interna: Optimierung von Bitmasken

Gast #1997485
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
Persönliche Seite #1999810
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
Gast #2000101
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!
Angehängte Dateien:
Persönliche Seite #2001836
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.

Antwort schreiben

Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt. Bitte hier nur auf die ursprüngliche Frage antworten, für neue Fragen einen neuen Beitrag erstellen.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.