mikrocontroller.net

Forum: Compiler & IDEs GCC generiert 2 Jumptables bei Switch


Autor: Dennis B. (higret)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

ich hab schon im Forum gesucht, aber nicht das richtig gefunden.

Ich hab in der main() eine Schleife in der eine Switch-Anweisung steht.

Der avr-gcc (GCC) 4.1.3 20070724 generiert daraus 2 Jumptables.

Das ganze wird für nen Atmega8 kompiliert

Hier 3 Ausschnitte
uint16_t t = ...;
switch (t) {
  case 0x01: set_act_date();
    break;
  case 0x02: get_act_date();
    break;
  case 0x03: get_alarm_info();
    break;
  case 0x04: set_alarm_info();
    break;
  case 0x05: write_alarm_with_index();
    break;
  case 0x06: get_alarm_with_index();
    break;
  case 0x07: add_alarm();
    break;
  case 0x08: delete_alarm_with_index();
    break;
  case 0x09: get_clock_config();
    break;
        case ...

}

in r31:r30 ist t abgelegt

mit dem ijmp springt er in den zweiten ASM-Ausschnitt zu Zeile 26+Index

und von da aus dann wieder zurück zur Zeile 65c
 64e:  31 97         sbiw  r30, 0x01  ; 1
 650:  e9 30         cpi  r30, 0x09  ; 9
 652:  f1 05         cpc  r31, r1
 654:  88 f7         brcc  .-30       ; 0x638 <main+0x8>
 656:  ed 5e         subi  r30, 0xED  ; 237
 658:  ff 4f         sbci  r31, 0xFF  ; 255
 65a:  09 94         ijmp
 65c:  5b df         rcall  .-330      ; 0x514 <set_act_date>
 65e:  ec cf         rjmp  .-40       ; 0x638 <main+0x8>
 660:  ad df         rcall  .-166      ; 0x5bc <get_act_date>
 662:  ea cf         rjmp  .-44       ; 0x638 <main+0x8>
 664:  7b df         rcall  .-266      ; 0x55c <get_alarm_info>
 666:  e8 cf         rjmp  .-48       ; 0x638 <main+0x8>
 668:  20 de         rcall  .-960      ; 0x2aa <set_alarm_info>
 66a:  e6 cf         rjmp  .-52       ; 0x638 <main+0x8>
 66c:  79 de         rcall  .-782      ; 0x360 <write_alarm_with_index>
 66e:  e4 cf         rjmp  .-56       ; 0x638 <main+0x8>
 670:  16 de         rcall  .-980      ; 0x29e <get_alarm_with_index>
 672:  e2 cf         rjmp  .-60       ; 0x638 <main+0x8>
 674:  33 de         rcall  .-922      ; 0x2dc <add_alarm>
 676:  e0 cf         rjmp  .-64       ; 0x638 <main+0x8>
 678:  2b df         rcall  .-426      ; 0x4d0 <get_clock_config>
 67a:  de cf         rjmp  .-68       ; 0x638 <main+0x8>

die rjmp zu main+0x8 gehen zur Anfang der Hauptschleife
   0:  1c c0         rjmp  .+56       ; 0x3a <__ctors_end>
   2:  2b c1         rjmp  .+598      ; 0x25a <__vector_1>
   4:  34 c0         rjmp  .+104      ; 0x6e <__bad_interrupt>
   6:  33 c0         rjmp  .+102      ; 0x6e <__bad_interrupt>
   8:  32 c0         rjmp  .+100      ; 0x6e <__bad_interrupt>
   a:  31 c0         rjmp  .+98       ; 0x6e <__bad_interrupt>
   c:  30 c0         rjmp  .+96       ; 0x6e <__bad_interrupt>
   e:  2f c0         rjmp  .+94       ; 0x6e <__bad_interrupt>
  10:  2e c0         rjmp  .+92       ; 0x6e <__bad_interrupt>
  12:  2d c0         rjmp  .+90       ; 0x6e <__bad_interrupt>
  14:  2c c0         rjmp  .+88       ; 0x6e <__bad_interrupt>
  16:  32 c3         rjmp  .+1636     ; 0x67c <__vector_11>
  18:  2a c0         rjmp  .+84       ; 0x6e <__bad_interrupt>
  1a:  29 c0         rjmp  .+82       ; 0x6e <__bad_interrupt>
  1c:  28 c0         rjmp  .+80       ; 0x6e <__bad_interrupt>
  1e:  27 c0         rjmp  .+78       ; 0x6e <__bad_interrupt>
  20:  26 c0         rjmp  .+76       ; 0x6e <__bad_interrupt>
  22:  25 c0         rjmp  .+74       ; 0x6e <__bad_interrupt>
  24:  24 c0         rjmp  .+72       ; 0x6e <__bad_interrupt>
  26:  1a c3         rjmp  .+1588     ; 0x65c <main+0x2c>
  28:  1b c3         rjmp  .+1590     ; 0x660 <main+0x30>
  2a:  1c c3         rjmp  .+1592     ; 0x664 <main+0x34>
  2c:  1d c3         rjmp  .+1594     ; 0x668 <main+0x38>
  2e:  1e c3         rjmp  .+1596     ; 0x66c <main+0x3c>
  30:  1f c3         rjmp  .+1598     ; 0x670 <main+0x40>
  32:  20 c3         rjmp  .+1600     ; 0x674 <main+0x44>
  34:  01 c3         rjmp  .+1538     ; 0x638 <main+0x8>
  36:  20 c3         rjmp  .+1600     ; 0x678 <main+0x48>

Wie kann ich den GCC dazu bringen, dass er direkt zur Zeile 65c springt 
und nicht erst den Umweg über den erweiterten Interrupt Vektor

lg

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

Bewertung
0 lesenswert
nicht lesenswert
Benimmt er sich falsch oder vermutest du nur anhand des etwas 
irreführenden Listings dass er es täte?

Denn bei
  676:  e0 cf         rjmp  .-64       ; 0x638 <main+0x8>
springt er natürlich nicht nach main+0x08, sondern nach 0x638 
(0x676+2-64).

Warum im Kommentar main+0x08 drinsteht weiss ich nicht. Aber nicht der 
Kommentar wird ausgeführt, sondern der Code.

Autor: Dennis B. (higret)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich weiß, aber 676+2-64 ist main+0x8 :)

daher der kommentar

main fängt bei 630 an


das ergebnis ist so wie gewollt, aber ich möchte einfach nur das, der µC 
nicht erst 2x springen muss bevor er dann die richtige funktion aufruft

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

Bewertung
0 lesenswert
nicht lesenswert
Dennis B. wrote:

> ich weiß, aber 676+2-64 ist main+0x8 :)

Mag sein. Aber drunter schreibst du was von der Interrupt-Tabelle. Und 
die hat ja nun rein garnichts mit der Sache zu tun. Denn wie du selber 
schreibst, geht der Sprung nach main+0x8=0x638 und nicht zur 
Interrupt-Tabelle.

Und da der wichtigste Teil des Codes fehlt, sowohl in Quelle wie im 
Listing, kann man auf die Frage schlecht eine Antwort gegen.

Autor: Dennis B. (higret)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hmm dachte das ist verständlich

weil mehr Quellcode posten bringt nix, weil der unwichtig ist.

Denn im ersten ASM Ausschnitt steht ein IJMP, und im Z Register steht

26 + (t-1) (aus der switch anweisung)

und zeile 26 steht im 2 ASM Code.

PS: aber wenn du möchtest, hänge ich gerne ca 1400 Zeilen Code an (400 
in C und 1000 in ASM)

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

Bewertung
0 lesenswert
nicht lesenswert
Ok, sorry, ich hatte das verkehrt gelesen.

Der Code hat nur eine einzige Sprungtabelle, nämlich die an 0x26. Das 
Zeug ab 0x65C ist schlicht der Code der einzelnen Komponenten vom 
Switch-Statement. Dass die alle ähnlich aussehen liegt an deinem ebenso 
ähnlichen Quellcode, nicht am Compiler.

Wenn du erreichen willst, dass in der Tabelle die Funktion direkt 
aufgerufen werden, dann solltest du hier kein switch() verwenden, 
sondern eine Funktionstabelle:
  typedef void (*funcptr)(void);
  void funcptr[9] = { set_act_date, get_act_date, ... };

  if (t >= 1 && t <= 9) {
     functab[t-1];
  }

Autor: Dennis B. (higret)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ah ok

danke


hab das jetzt mit nem Funktionspointer-Array gemacht. Der Code ist um ca 
80 Words kleiner.

Danke

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wie kann ich den GCC dazu bringen, dass er direkt zur Zeile 65c springt
>und nicht erst den Umweg über den erweiterten Interrupt Vektor

Aus akademischem Interesse ja ganz interessant, aber gibt es bei deiner 
Anwendung auch einen praktischen Grund?

Oliver

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Code ist um ca 80 Words kleiner.

Ich denke mal, das war der praktische Grund...

Autor: Dennis B. (higret)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja da liegt Klaus richtig, der Code sollte kleiner werden und schneller 
ist der Code wohl auch.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der GCC hat für berechnete Switch-Anweisungen ein festes Code-Muster,
das einen IJMP in eine Serie von RJMPs vorsieht. Das ist für den Fall,
das in den einzelnen Cases unterschiedlich lange Anweisungen stehen,
auch optimal. In deinem Fall steht aber in jedem Case nur ein
einzelner RCALL, der natürlich mit dem RJMP aus der Tabelle zu einem
einzelnen Befehl vereint werden könnte.

Das wäre eine zusätzliche Optimierungregel, die aber offensichtlich
nicht implementiert ist. Vielleicht haben die Entwickler auch deswegen
darauf verzichtet, weil solche Konstrukte sowieso meist mit einem
Array aus Funktionspointern realisiert werden, wie du es jetzt ja
schließlich ebenfalls getan hast.

Die Funktionspointerlösung hat den den Vorteil, dass der Sourcecode
kürzer wird, und die Ausführungszeit ist auch dann unabhängig von der
Anzahl der unterschiedlichen Werte von t, wenn der Compiler bei
Switches keine Optimierung durch berechnete Sprünge kann.

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.