mikrocontroller.net

Forum: Compiler & IDEs switch Case läuft nicht im AVR Simulator


Autor: Ganymed (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen

Ich wollte einmal analysieren, wie GCC ein Programm aufbaut,
bei dem in Abhängigkeit von einer Variablen eine bestimmte
Funktion aufgerufen wird.
Zuerst habe ich ein Programm geschrieben, dass ein Array von Funktions-
zeigern nutzt. Hat im Simulator prima geklappt. (Optimierung auf -Os)

Dann habe ich den Arrayaufruf durch eine switch case Anweisung
ersetzt (siehe unten). Mich interessiert, wie sich jetzt der 
Maschinencode
verändert.
Nun das Problem:
Das Programm arbeitet im Simulator nicht unter –Os ! Die Zählschleife
wird sauber durchgezählt (0,1,2,3) aber es wird nur die Funktion F_2
ausgeführt.
Stelle ich die Optimierung auf –O0 läuft es. Das nützt mir aber nichts,
da ich auch den Speicherbedarf und die Laufzeit beider Versionen
vergleichen möchte.

Wie bekomme ich das Programm in –Os im Simulator zum laufen?
Woran erkennt der Compiler, dass es eigentlich ein sinnloses Programm 
ist?

Die NOP-Funktion benutze ich als Orientierung in der LSS-Datei.

#include <avr/io.h>
#define nop asm volatile ("nop")

void F_0(char);
void F_1(char);
void F_2(char);
void F_3(char);


int main(void)
{

 char i;

 while(1)
 {
   for(i=0; i<4; i++)
   {
    nop;

    switch(i)
    {
     case 0 : F_0(i); break;
     case 1 : F_1(i); break;
     case 2 : F_2(i); break;
     case 3 : F_3(i); break;
    }

   }
 }
}


void F_0 (char x0)
{
 nop;
 x0++;
 x0 = x0>>2;
 PORTA = x0;
}


void F_1 (char x1)
{
 nop;
 x1 += 5;
  PORTB = x1;
}


void F_2 (char x2)
{
 nop;
 x2++;
 x2 += x2 | 0b00001111;
 PORTC = x2;
}


void F_3 (char x3)
{
 nop;
 x3++;
 x3 &= 0xF0;
 PORTD = x3;
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist hier völlig ok. Der Compiler inlined die Funktionen und sortiert das 
sinnlose Variablengewusel komplett aus. Bleiben die Ausgabebefehle mit 
konstanten Daten und die NOPs übrig.

Diese Runde im klassischen Spiel Compiler gegen Programmierer ging 
jedenfalls klar an den Compiler.

Autor: Ganymed (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke erste einmal
Bleiben für mich zwei Fragen:


1) Warum wird bei meiner Lösung mit dem Array nichts aussortiert?
  (Programm siehe unten)

2) Woran will der Compiler die Sinnlosigkeit erkennen?
   Ich hab schon viel analysiert. Führt man das Ergebnis
   einer Berechnung auf ein Hardware-Port / Zähler u.s.w
   sortiert er i.n.R. nicht aus.

Mein Programm mit dem Array:

#include <avr/io.h>
#define nop asm volatile ("nop")

typedef void (*pFunc)(char);

void F_0(char);
void F_1(char);
void F_2(char);
void F_3(char);


pFunc Arr[4] = {F_0, F_1, F_2, F_3};

int main(void)
{

 unsigned char i;

 while(1)
 {
   for(i=0; i<4; i++)
   {
    nop;
    Arr[i](i);
   }
 }
}


void F_0 (char x0)
{
 nop;
 x0++;
 x0 = x0>>2;
 PORTC = x0;
}

void F_1 (char x1)
{
 nop;
 x1 += 5;
  PORTC = x1;
}

void F_2 (char x2)
{
 nop;
 x2++;
 x2 += x2 | 0b00001111;
 PORTC = x2;
}


void F_3 (char x3)
{
 nop;
 x3++;
 x3 &= 0xF0;
 PORTC = x3;
}

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganymed schrieb:

> Woran erkennt der Compiler, dass es eigentlich ein sinnloses Programm
> ist?

Inlining der Funktionen. Wenn klein genug, wird der Code direkt 
eingebaut und nicht als Funktion aufgerufen. Damit ist er dem beim GCC 
recht weit entwickelten Optimizer zugänglich und das Ergebnis sieht man.

Abhilfe: -fno-inline-small-functions.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganymed schrieb:

> 1) Warum wird bei meiner Lösung mit dem Array nichts aussortiert?
>   (Programm siehe unten)

Weil der Compiler die Funktionen dieses Mal nicht inlinen kann.

>    Ich hab schon viel analysiert. Führt man das Ergebnis
>    einer Berechnung auf ein Hardware-Port / Zähler u.s.w
>    sortiert er i.n.R. nicht aus.

Bei steigender Komplexität einer Funktion ist irgendwann die Schwelle 
zum nicht mehr sinnvollem Inlining überschritten. Diese Kalkulation ist 
allerdings alles andere als perfekt. Zumal er diese Entscheidung 
möglicherweise vor der Optimierung trifft.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganymed schrieb:
> 1) Warum wird bei meiner Lösung mit dem Array nichts aussortiert?
>   (Programm siehe unten)

Die Werte in Arr[] sind zur Compilezeit nicht bekannt. Es ist denkbar, 
daß sie vor der Verwendung in main in einem anderen Modul verändert 
werden.

Um die Optmierung prinzipiell zu ermöglichen müsste Arr[] read-only 
sein, also const.

Allerdings ist selbst dann einiges zu tun für einen Compiler, um das 
dann zu optimiern:
-- die i-Schleife muss aufgerollt werden
-- es muss erkannt werden, daß Arr[i] zur Compilezeit bekannte werte hat
-- Die indirekten Aufrufe Arr[i]() müssten in direkte umgewandelt
   werden
-- ggf. Arr[i] inlinen
-- weiter optimieren

GCC 4.3 ist noch nicht so weit.

Johann

Autor: Ganymed (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>GCC 4.3 ist noch nicht so weit.

Aber schon verdammt weit! Hut ab vor den
Programmieren kann ich da nur sagen.

Das mit dem Inlinimg der Funktionen ist
mir jetzt klar und habe ich in meinem
Code auch schon beobachtet.
Warum aber, in meinem switch-case-Prog.,
die Funktion F_3 aussortiert wird und die F_2
nicht, ist mir immer noch nicht klar.
Die Funktionen haben die gleiche "Kömplexität"
und greifen auf verschiedene Ports zu.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
F2: x2 += x2 | 0b00001111;
Das sind 2 Operationen:
  temp = x2 | const;
  x2 = x2 + temp;

F3: x3 &= 0xF0;
Und das ist nur eine Operation.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganymed schrieb:

> Warum aber, in meinem switch-case-Prog.,
> die Funktion F_3 aussortiert wird und die F_2
> nicht, ist mir immer noch nicht klar.
> Die Funktionen haben die gleiche "Kömplexität"
> und greifen auf verschiedene Ports zu.

Nein, F2 ist komplexer. Es hat mehr Operationen. Nach den Tree-Passes 
sehen F2/F3 so aus:
;; Function F_2 (F_2)

F_2 (x2)
{
  char x2.28;
  unsigned char D.1254;

<bb 2>:
  __asm__ __volatile__("nop"::);
  x2.28 = x2 + 1;
  D.1254 = (unsigned char) (x2.28 | 15) + (unsigned char) x2.28;
  *53B ={v} D.1254;
  return;
}

;; Function F_3 (F_3)

F_3 (x3)
{
  char x3.34;
  unsigned char x3.5;

<bb 2>:
  __asm__ __volatile__("nop"::);
  x3.34 = x3 + 1;
  x3.5 = (volatile uint8_t) (x3.34 & -16);
  *50B ={v} x3.5;
  return;
}

Dies führt dazu, daß F2 durch 11 Insns dargestellt wird, F3 jedoch durch 
nur 10 Insns.

Wenn du wirklich nachverfolgen willst, wie gcc an dem Code rumbastelt, 
kannst du Dumps erzeugen lassen mit.
   -fdump-tree-all-details 
   -fdump-rtl-all-details
   -save-temps
   -fverbose-asm
   -dP

Wenn -dP zu viel des Guten ist im Assembler (*.s), dann einfach 
weglassen oder -dp angeben stattdessen.

Die Tree-Dumps sind C-ähnlich und lassen sich problemlos verstehen. 
Falls Fragen zu RTL sind kann ich die gerne beantworten.

Johann

Autor: Ganymed (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke an alle :-)
Meine Fragen sind beantwortet.

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.