mikrocontroller.net

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


Autor: Robert S. (razer) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
{ 
  char tem[2];
  temp->_int -= 4000;
  tem[0] = temp->_int / 100;  //ganz
  tem[1] = temp->_int % 100;  //komma
  temp->ganz = tem[0];
  temp->komma = tem[1];
}

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

Ist da ein Compilerbug?

Gruß Robert

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Opfern von inlining? Disassembly sieht da oft etwas merkwürdig aus.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert S. (razer) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
{ 
  char tem[2];
  temp->_int -= 4000;
  tem[0] = temp->_int / 100;  //ganz
  tem[1] = temp->_int % 100;  //komma
  temp->ganz = tem[0];
  temp->komma = tem[1];
  
  uint16_t test = (uint16_t) (-40000000 + 405000 * hum->_int - 28 * (uint32_t) hum->_int * hum->_int) / 100000;
}
void sht_raw_to_physical(sht_val *temp, sht_val *hum)
{ 
 23e:  fc 01         movw  r30, r24
  char tem[2];
  temp->_int -= 4000;
 240:  80 81         ld  r24, Z
 242:  91 81         ldd  r25, Z+1  ; 0x01
 244:  80 5a         subi  r24, 0xA0  ; 160
 246:  9f 40         sbci  r25, 0x0F  ; 15
 248:  91 83         std  Z+1, r25  ; 0x01
 24a:  80 83         st  Z, r24
  tem[0] = temp->_int / 100;  //ganz
  tem[1] = temp->_int % 100;  //komma
  temp->ganz = tem[0];
  temp->komma = tem[1];
 24c:  64 e6         ldi  r22, 0x64  ; 100
 24e:  70 e0         ldi  r23, 0x00  ; 0
 250:  25 d0         rcall  .+74       ; 0x29c <__udivmodhi4>
 252:  80 83         st  Z, r24
 254:  08 95         ret

00000256 <_sht_get_measurement_data>:
  
  uint16_t test = (uint16_t) (-40000000 + 405000 * hum->_int - 28 * (uint32_t) hum->_int * hum->_int) / 100000;
}

Was passiert da mit der Berechnung?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).
.global sht_raw_to_physical
        .type   sht_raw_to_physical, @function
sht_raw_to_physical:
/* prologue: frame size=0 */
/* prologue end (size=0) */
        movw r30,r24
        ld r24,Z
        ldd r25,Z+1
        subi r24,lo8(-(-4000))
        sbci r25,hi8(-(-4000))
        std Z+1,r25
        st Z,r24
        ldi r22,lo8(100)
        ldi r23,hi8(100)
        rcall __udivmodhi4
        st Z,r24
/* epilogue: frame size=0 */
        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.

Autor: Robert S. (razer) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Murks (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
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.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.