Forum: Compiler & IDEs Was macht bitte der GCC da?? Compilerbug?


von Robert S. (razer) Benutzerseite


Lesenswert?

Hallo an alle,

Ich benutz das WinAvr 20070525 Packet mit dem GCC 4.1.2. Optimierung: s

Ich hab eine Funktion geschrieben. Sie ist zwar noch nicht fertig, aber 
egal.
1
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
2
{ 
3
  char tem[2];
4
  temp->_int -= 4000;
5
  tem[0] = temp->_int / 100;  //ganz
6
  tem[1] = temp->_int % 100;  //komma
7
  temp->ganz = tem[0];
8
  temp->komma = tem[1];
9
}

Nun hab ich mehr. Was macht der Compiler da??
1
 246:sht1c.c       **** void sht_raw_to_physical(sht_val *temp, sht_val *hum)
2
 247:sht1c.c       **** { 
3
 750                   mov r15,r24
4
 751                   ldi r18,lo8(0)
5
 752                   ldi r19,hi8(0)
6
 753                   ldi r25,lo8(16)
7
 754 01de FC01        .L39:
8
 248:sht1c.c       ****   char tem[2];
9
 249:sht1c.c       ****   temp->_int -= 4000;
10
 755                 (3)
11
 756                   rjmp .L46
12
 757 01e0 8081        .L36:
13
 759 01e4 805A        .LM110:
14
 760 01e6 9F40          ldi r24,lo8(5)
15
 761 01e8 9183        .L46:
16
 762 01ea 8083          rcall sht_write_byte
17
 250:sht1c.c       ****   tem[0] = temp->_int / 100;  //ganz
18
 251:sht1c.c       ****   tem[1] = temp->_int % 100;  //komma
19
 252:sht1c.c       ****   temp->ganz = tem[0];
20
 253:sht1c.c       ****   temp->komma = tem[1];
21
 763                 t r17
22
 764                   breq .L36
23
 766 01ee 70E0        .LM109:
24
 767 01f0 00D0          ldi r24,lo8(3)
25
 768 01f2 8083          rjmp .L46
26
 769                 .L36:
27
 771                 .LM110:
28
 772                   ldi r24,lo8(5)
29
 773                 .L46:
30
 774                   rcall sht_write_byte
31
 775                   mov r15,r24
32
 776                   ldi r18,lo8(0)
33
 777                   ldi r19,hi8(0)
34
 778                   ldi r25,lo8(16)
35
 779                 .L39:
36
 254:sht1c.c       ****   
37
 255:sht1c.c       **** }

Ist da ein Compilerbug?

Gruß Robert

von Andreas K. (a-k)


Lesenswert?

Opfern von inlining? Disassembly sieht da oft etwas merkwürdig aus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Kannst du bitte erst einmal erklären, was denn genau dein Problem
ist?

Am besten immer mit compilierfähigem Code belegen, damit man das
nachvollziehen kann.

Das Stückchen Disassembler-Listing da verwirrt eher, als dass es
mir irgendwie hilft zu verstehen, was du hier eigentlich erwartest
und was dann nicht in Erfüllung gegangen ist.

Btw., / und % sind beides Divisionen und damit teure Operationen.
Da eine Division immer auch den Rest gleichzeitig mit berechnet,
kann man das durch die Funktion div() in einem Schritt erledigen --
spart einen Aufruf der Divisionsfunktion.

von Robert S. (razer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Ich dachte im Disassembler Listing müsste ähnliches wie im Assembler 
Listing stehen.

Hier hab ich mal ein C Beispiel, und dann das Assembler Listing:
1
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
2
{ 
3
  char tem[2];
4
  temp->_int -= 4000;
5
  tem[0] = temp->_int / 100;  //ganz
6
  tem[1] = temp->_int % 100;  //komma
7
  temp->ganz = tem[0];
8
  temp->komma = tem[1];
9
  
10
  uint16_t test = (uint16_t) (-40000000 + 405000 * hum->_int - 28 * (uint32_t) hum->_int * hum->_int) / 100000;
11
}
1
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
2
{ 
3
 23e:  fc 01         movw  r30, r24
4
  char tem[2];
5
  temp->_int -= 4000;
6
 240:  80 81         ld  r24, Z
7
 242:  91 81         ldd  r25, Z+1  ; 0x01
8
 244:  80 5a         subi  r24, 0xA0  ; 160
9
 246:  9f 40         sbci  r25, 0x0F  ; 15
10
 248:  91 83         std  Z+1, r25  ; 0x01
11
 24a:  80 83         st  Z, r24
12
  tem[0] = temp->_int / 100;  //ganz
13
  tem[1] = temp->_int % 100;  //komma
14
  temp->ganz = tem[0];
15
  temp->komma = tem[1];
16
 24c:  64 e6         ldi  r22, 0x64  ; 100
17
 24e:  70 e0         ldi  r23, 0x00  ; 0
18
 250:  25 d0         rcall  .+74       ; 0x29c <__udivmodhi4>
19
 252:  80 83         st  Z, r24
20
 254:  08 95         ret
21
22
00000256 <_sht_get_measurement_data>:
23
  
24
  uint16_t test = (uint16_t) (-40000000 + 405000 * hum->_int - 28 * (uint32_t) hum->_int * hum->_int) / 100000;
25
}

Was passiert da mit der Berechnung?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Lass doch mal den Disassembler-Krempel weg, und guck dir an, was der
Compiler für diese Funktion als Assemblercode generiert (Kommando
"make sht1c.s", wobei ich es ohne -gstabs laufen lassen habe, damit es
übersichtlicher wird).
1
.global sht_raw_to_physical
2
        .type   sht_raw_to_physical, @function
3
sht_raw_to_physical:
4
/* prologue: frame size=0 */
5
/* prologue end (size=0) */
6
        movw r30,r24
7
        ld r24,Z
8
        ldd r25,Z+1
9
        subi r24,lo8(-(-4000))
10
        sbci r25,hi8(-(-4000))
11
        std Z+1,r25
12
        st Z,r24
13
        ldi r22,lo8(100)
14
        ldi r23,hi8(100)
15
        rcall __udivmodhi4
16
        st Z,r24
17
/* epilogue: frame size=0 */
18
        ret

In der ersten Zeile wird der Z-Pointer mit dem ersten Argument (temp)
geladen.  In der zweiten bis siegenten Zeile wird temp->_int gelesen,
4000 subtrahiert, und zurückgespeichert.  Das Ergebnis steht gleich
als erstes Argument für die interne Funktion __udivmodhi4 in den
richtigen Registern, es muss danach nur noch der Dividend 100 in
Register r22/r23 geladen werden, danach kann die Division aufgerufen
werden.  Das Ergebnis wird dann nach temp->komma abgespeichert.

Nun verwundert natürlich auf den ersten Blick, was er hier mit
temp->komma macht, nicht wahr?  Das verwundert nicht mehr, wenn man in
den Code reinguckt, wie sht_val definiert ist.  Das ist nämlich eine
union, d. h. die Elemente "ganz" und "komma" belegen den gleichen
Speicherplatz!  Der Compiler erkennt also richtig, dass er den Wert
von "ganz" gar nicht erst abspeichern muss, da der danach sowieso
durch "komma" überschrieben wird.  Das ist vermutlich nicht ganz das,
was du wolltest...

Der Compiler hätte übrigens noch etwas besser optimieren können: die
Zuweisung von r24 nach Z vor dem Aufruf der Division ist unnötig,
da dieses Byte danach ja wieder zerstört wird.  Das hätte er ggf.
herausfinden können, indem man "temp" mit dem Schlüsselwort
"restrict" gekennzeichnet hätte (C99-Feature); macht er aber selbst
dann nicht, weil er die Zuweisung der beiden Bytes einer 16-Bit-Zahl
nicht trennen kann und die Zuweisung von r25 nach Z+1 nicht
redundant ist.

Die lokale Variable test wird gar nicht erst berechnet, da der
Optimierer feststellt, dass die Berechnung komplett für die Katz' wäre
-- sie wird ja nirgends benutzt.

von Robert S. (razer) Benutzerseite


Lesenswert?

Ok, danke. Jetzt is alles klar :) Verwende ich test, Kommt auch die 
Berechnung rein.

Ich hab noch eine Frage. Ich hab da ja eigentlich sehr koplizierte 
Rechnung. Ich hab das auf Integer erweitert und dann die Division.

Kann ich da noch was für den Avr optimieren?

von Murks (Gast)


Lesenswert?

>Kann ich da noch was für den Avr optimieren?

Ja. Deine Denk- und Ausdrucksweise! Die beste Opimierungsmöglichkeit 
ist, das jemandem zu überlassen, der es kann.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Kann ich da noch was für den Avr optimieren?

Naja, du arbeitest (in deiner nachfolgenden Rechnung) implizit mit
32-bit-Integern, die sind ziemlich langwierig, da sie in der
Bibliothek wenig optimiert sind.

Andererseits ist es ja immer eine Frage, ob die erreichte Performance
letztlich deinen Anwendungszweck erfüllt.  Die AVRs sind ja nicht
gerade extrem langsam.

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.