Forum: Compiler & IDEs avr-gcc: mal lds, mal ld rd, Z


von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Hallo zusammen,

da ich immer mal einen Blick auf den vom GCC erzeugten Assembler-Code 
werfe (man lernt ja nie aus), ist mir heute folgendes aufgefallen:

simpelste Instruktionen zur UART-Steuerung werden verschieden codiert:
1
UCSR0B &= ~_BV(UDRIE0);
wird zu
1
lds r24,193   ;  D.1563, MEM[(volatile uint8_t *)193B]
2
andi r24,lo8(-33)   ;  D.1563,
3
sts 193,r24   ;  MEM[(volatile uint8_t *)193B], D.1563

während
1
UCSR0B |= _BV(TXEN0);
so übersetzt wird:
1
ldi r30,lo8(-63)   ;  tmp47,
2
ldi r31,0   ; 
3
ld r24,Z   ;  D.1568, MEM[(volatile uint8_t *)193B]
4
ori r24,lo8(8)   ;  D.1568,
5
st Z,r24   ;  MEM[(volatile uint8_t *)193B], D.1568
(das Z-Register wird nachher nicht weiter verwendet, unmittelbar danach 
folgt ein ret)

ich würde gerne verstehen warum im zweiten Fall nicht auch mit lds/sts 
gearbeitet wird?

Danke, Michi

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael Reinelt schrieb:

> ich würde gerne verstehen warum im zweiten Fall nicht auch mit lds/sts
> gearbeitet wird?

Erste Hinweise geben dir die Dumps, welche mit -fdump-rtl-all 
-fdump-tree-all erzeugt werden können.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:
> Erste Hinweise geben dir die Dumps, welche mit -fdump-rtl-all
> -fdump-tree-all erzeugt werden können.

;-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Johann L. schrieb:

> Erste Hinweise geben dir die Dumps, welche mit -fdump-rtl-all
> -fdump-tree-all erzeugt werden können.

Ähhhh.... ich fürchte das sind eine Spur zu viele Hinweise :-) Der 
erzeugt mir jetzt ja gefühlte 2000 Dumps :-(

Hat jemand eine idee wo ungefähr ich da reinschauen sollte?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael Reinelt schrieb:
> Johann L. schrieb:
>
>> Erste Hinweise geben dir die Dumps, welche mit -fdump-rtl-all
>> -fdump-tree-all erzeugt werden können.
>
> Ähhhh.... ich fürchte das sind eine Spur zu viele Hinweise :-) Der
> erzeugt mir jetzt ja gefühlte 2000 Dumps :-(

Schamlose Übertreibnug, da sind maximal 150-200 Dumps, mehr nicht.
>
> Hat jemand eine idee wo ungefähr ich da reinschauen sollte?

Schau dir an, wo die Ersetzung im erwarteten Fall (nicht) stattfindet 
und vergleiche das mit dem unliebsamen Fall.

Wenn der Pass gefunden ist, in dem die Ersetzung (nicht) stattfindet, 
dann

1) Ist das der Pass, in dem die Ersetzung wirklich stattfinden soll? 
IIRC wird teilweise in insn combine ersetzt, was aber mir aber nicht 
zielführend erscheint.

2) Wenn der Pass gefunden ist, gehst du in die Quellen und suchst dort 
den Übeltäter und erstellst einen Fix / ein Patch nebst Testfall :-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Johann L. schrieb:
> Michael Reinelt schrieb:
>> Johann L. schrieb:
>>
>>> Erste Hinweise geben dir die Dumps, welche mit -fdump-rtl-all
>>> -fdump-tree-all erzeugt werden können.
>>
>> Ähhhh.... ich fürchte das sind eine Spur zu viele Hinweise :-) Der
>> erzeugt mir jetzt ja gefühlte 2000 Dumps :-(
>
> Schamlose Übertreibnug, da sind maximal 150-200 Dumps, mehr nicht.

ich sagte ja "gefühlt" :-)

>> Hat jemand eine idee wo ungefähr ich da reinschauen sollte?
>
> Schau dir an, wo die Ersetzung im erwarteten Fall (nicht) stattfindet
> und vergleiche das mit dem unliebsamen Fall.
>
> Wenn der Pass gefunden ist, in dem die Ersetzung (nicht) stattfindet,
> dann
>
> 1) Ist das der Pass, in dem die Ersetzung wirklich stattfinden soll?
> IIRC wird teilweise in insn combine ersetzt, was aber mir aber nicht
> zielführend erscheint.
>
> 2) Wenn der Pass gefunden ist, gehst du in die Quellen und suchst dort
> den Übeltäter und erstellst einen Fix / ein Patch nebst Testfall :-)

na du machst mir Spaß :-)

Aber da ich an chronischer Neugierde im Endstadium leide, werd ich mir 
das antun... aber erst morgen.

Ich kann zwar RTL genau überhaupt nicht, aber ich vermute das steht für 
"Register tranfer language", was so ein Zwischenformat des GCC ist.

Es gibt auch so einen Modus "-dP" oder so, wo er in den Assembler-Code 
die RTL-Statements als Kommentare reinzieht. Da hab ich heut mal kurz 
drübergeschaut (hab leider das Ergebnis grad nicht bei der Hand), aber 
die RTL-Statements sahen für mich zumindest ähnlich aus. Daher meine 
Frage: kann es sein dass der Unterschied gar nicht in der RTL 
aufscheint, sondern erst später, wo RTL in den tatsächlichen Code 
übersetzt wird? (RTL ist ja ein Stück weit "prozessor-neutral"?)

Was ich auch noch prüfen möchte: Der Code mit Z-Pointer sieht zwar 
komplexer aus, aber diese Operationen laufen ja schneller als ein "lds". 
Vielleicht ist es sowohl von code-size als auch von cycles Jacke wie 
Hose, welcher Code erzeugt wird?

Vielleicht hängt es auch damit zusammen, dass eine Variante eine 
"normale" Funktion ist, während die andere aus einer ISR kommt?

Ich muss morgen auf jeden Fall einen echten, vereinfachten Testcase 
erstellen....

anyway: vielen Dank für deine Unterstützung!

von (prx) A. K. (prx)


Lesenswert?

Michael Reinelt schrieb:
> Vielleicht ist es sowohl von code-size als auch von cycles Jacke wie
> Hose, welcher Code erzeugt wird?

Und genau so ist es.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

A. K. schrieb:
> Michael Reinelt schrieb:
>> Vielleicht ist es sowohl von code-size als auch von cycles Jacke wie
>> Hose, welcher Code erzeugt wird?
>
> Und genau so ist es.

hast du nachgezählt?

von (prx) A. K. (prx)


Lesenswert?

Michael Reinelt schrieb:
> hast du nachgezählt?

LDS/STS 2 Takte 2 Worte, der Rest 1 Takt 1 Wort. Zähl selbst.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael Reinelt schrieb:
> Johann L. schrieb:
>> Michael Reinelt schrieb:
>>> Johann L. schrieb:
>>> Hat jemand eine idee wo ungefähr ich da reinschauen sollte?
>>
>> Schau dir an, wo die Ersetzung im erwarteten Fall (nicht) stattfindet
>> und vergleiche das mit dem unliebsamen Fall.
>>
>> Wenn der Pass gefunden ist, in dem die Ersetzung (nicht) stattfindet,
>> dann
>>
>> 1) Ist das der Pass, in dem die Ersetzung wirklich stattfinden soll?
>> IIRC wird teilweise in insn combine ersetzt, was aber mir aber nicht
>> zielführend erscheint.
>>
>> 2) Wenn der Pass gefunden ist, gehst du in die Quellen und suchst dort
>> den Übeltäter und erstellst einen Fix / ein Patch nebst Testfall :-)
>
> na du machst mir Spaß :-)

Naja, weg Fragt muß auch mit einer Antwort rechnen :-P

> Ich kann zwar RTL genau überhaupt nicht, aber ich vermute das steht für
> "Register tranfer language", was so ein Zwischenformat des GCC ist.

Jepp

> Es gibt auch so einen Modus "-dP" oder so, wo er in den Assembler-Code
> die RTL-Statements als Kommentare reinzieht. Da hab ich heut mal kurz
> drübergeschaut (hab leider das Ergebnis grad nicht bei der Hand), aber
> die RTL-Statements sahen für mich zumindest ähnlich aus.

Einmal müsste es eine indirekte Adressierung sein wie
1
(set (reg:QI Rn)
2
     (mem:QI (reg:HI r30)))
was ein "LD Rn, Z" bedeutet. Im Fall einer direkten Adressierung mit zur 
Compilezeit bekannter Adresse sowas wie
1
(set (reg:QI Rn)
2
     (mem:QI (const_int XYZ)))
Was ein "LDS Rn, XYZ" bedeutet.

Für die Maschinenmodi siehe z.B.

http://gcc.gnu.org/onlinedocs/gccint/Machine-Modes.html#Machine-Modes
http://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention

> Daher meine
> Frage: kann es sein dass der Unterschied gar nicht in der RTL
> aufscheint, sondern erst später, wo RTL in den tatsächlichen Code
> übersetzt wird? (RTL ist ja ein Stück weit "prozessor-neutral"?)

Nein. Idee und Ziel von RTL ist, eine arithmetische / algebraische 
Beschreibung einer Assembler-Instruktion darzustellen.  Im Laufe der 
RTL-Passes werden die insns immer weiter in Richtung "echter" 
Instruktionen umgewandelt und im lezten Pass lediglich in Text (s-File) 
umgewandelt. Das -dP Dump enthält die letzten insns als Kommentar und 
nachfolgend die entsprechende(n) Assembler-Instruktione(n).

Die Syntax und Semantik von RTL ist Target-übergreifend. Target-abhängig 
ist hingegen, welche RTL-Konstrukte erlaubt sind. Was in (mem:QI 
(const_int 0x1234)) bedeutet ist z.B. Target-unabhängig, allerdings ist 
so ein Speicherzugriff nur dann gültig, wenn das Target die 
entsprechende Adressierungsart auch unterstützt.

> Was ich auch noch prüfen möchte: Der Code mit Z-Pointer sieht zwar
> komplexer aus, aber diese Operationen laufen ja schneller als ein "lds".
> Vielleicht ist es sowohl von code-size als auch von cycles Jacke wie
> Hose, welcher Code erzeugt wird?

Siehe unten.

> Vielleicht hängt es auch damit zusammen, dass eine Variante eine
> "normale" Funktion ist, während die andere aus einer ISR kommt?

Nö, das spielt keine Rolle. Eher von bedeutung ist volatile.


A. K. schrieb:
> Michael Reinelt schrieb:
>> Vielleicht ist es sowohl von code-size als auch von cycles Jacke wie
>> Hose, welcher Code erzeugt wird?
>
> Und genau so ist es.

Ich habe aber auch schon Fälle gesehen, wo es ungünstiger ist, und von 
der Registerlast her ist es auf jeden Fall schlecht.  Dass es hier nicht 
mieser ist als direkte Adressierung ist eher Zufall aber kein Design.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

So, ich hab mal einen testcase vorbereitet:
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
volatile uint8_t flag;
5
6
void test_a(void)
7
{
8
    UCSR0B |= _BV(TXEN0);
9
}
10
11
12
void test_b(void)
13
{
14
    if (flag) {
15
  UCSR0B |= _BV(TXEN0);
16
    }
17
}

das "if(flag)" ist notwendig, sonst kompiliert er beide Funktionen 
gleich)

Ergebnis:
1
  .text
2
.global  test_a
3
  .type  test_a, @function
4
test_a:
5
/* prologue: function */
6
/* frame size = 0 */
7
/* stack size = 0 */
8
.L__stack_usage = 0
9
  ldi r30,lo8(-63)   ;  tmp44,
10
  ldi r31,0   ; 
11
  ld r24,Z   ;  D.1400, MEM[(volatile uint8_t *)193B]
12
  ori r24,lo8(8)   ;  D.1400,
13
  st Z,r24   ;  MEM[(volatile uint8_t *)193B], D.1400
14
  ret
15
  .size  test_a, .-test_a
16
.global  test_b
17
  .type  test_b, @function
18
test_b:
19
/* prologue: function */
20
/* frame size = 0 */
21
/* stack size = 0 */
22
.L__stack_usage = 0
23
  lds r24,flag   ;  flag.0, flag
24
  tst r24   ;  flag.0
25
  breq .L2   ; ,
26
  lds r24,193   ;  D.1397, MEM[(volatile uint8_t *)193B]
27
  ori r24,lo8(8)   ;  D.1397,
28
  sts 193,r24   ;  MEM[(volatile uint8_t *)193B], D.1397
29
.L2:
30
  ret
31
  .size  test_b, .-test_b
32
  .comm  flag,1,1
33
  .ident  "GCC: (GNU) 4.7.2"
34
.global __do_clear_bss

und jetzt zum RTL: die relevanten Stellen habe ich aus -dP rauskopiert:
1
 ; (insn 6 5 7 (set (reg:QI 24 r24 [orig:43 D.1400 ] [43])
2
 ;         (mem/v:QI (reg/f:HI 30 r30 [44]) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])) uart.c:18 28 {movqi_insn}
3
 ;      (nil))
4
  ld r24,Z   ;  D.1400, MEM[(volatile uint8_t *)193B]   ;  6  movqi_insn/4  [length = 1]
und
1
 ; (insn 10 8 11 (set (reg:QI 24 r24 [orig:44 D.1397 ] [44])
2
 ;         (mem/v:QI (const_int 193 [0xc1]) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])) uart.c:25 28 {movqi_insn}
3
 ;      (nil))
4
  lds r24,193   ;  D.1397, MEM[(volatile uint8_t *)193B]   ;  10  movqi_insn/4  [length = 2]

Und hier gibts tatsächlich einen unterschied:
einmal ist es
set (reg:QI 24 r24) (mem/v:QI (reg/f:HI 30 r30 [44]))
und das zweitmal
(set (reg:QI 24 r24) (mem/v:QI (const_int 193 [0xc1]))

jetzt weiss ich aber nicht mehr weiter :-(

Johann, magst du mir nochmal helfen bitte?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

A. K. schrieb:
> LDS/STS 2 Takte 2 Worte, der Rest 1 Takt 1 Wort. Zähl selbst.

Ich hab nachgezählt:

LDS 2W 2Cycle
ORI 1W 1C
STS 2W 2C
---------
    5W 5C

LDI r30 1W 1C
LDI r31 1W 1C
LD,Z    1W 1C
ORI     1W 1C
ST,Z    1W 2C
-------------
        5W 6C

der "komplizierte" Code ist zwar gleich lang (5 Wörter) braucht aber 
einen takt länger. oder hab ich mich verzählt?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

ich hab mir tatsächlich das RTL angetan :-) und zumindest die Stelle 
gefunden wo der Unterschied auftritt:

nach pass 161 (forward propagation) sieht der Code noch gleich aus, 
interessanterweise beide male noch mit "indirekter" Adressierung:
1
(insn 5 2 6 2 (set (reg/f:HI 44)
2
        (const_int 193 [0xc1])) test1.c:18 32 {*movhi}
3
     (nil))
4
5
(insn 6 5 7 2 (set (reg:QI 42 [ D.1390 ])
6
        (mem/v:QI (reg/f:HI 44) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])) test1.c:18 28 {movqi_insn}
7
     (nil))
8
9
(insn 7 6 9 2 (set (reg:QI 43 [ D.1391 ])
10
        (ior:QI (reg:QI 42 [ D.1390 ])
11
            (const_int 8 [0x8]))) test1.c:18 178 {iorqi3}
12
     (expr_list:REG_DEAD (reg:QI 42 [ D.1390 ])
13
        (nil)))
14
15
(insn 9 7 0 2 (set (mem/v:QI (reg/f:HI 44) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])
16
        (reg:QI 43 [ D.1391 ])) test1.c:18 28 {movqi_insn}
17
     (expr_list:REG_DEAD (reg/f:HI 44)
18
        (expr_list:REG_DEAD (reg:QI 43 [ D.1391 ])
19
            (nil))))


nach pass 162 (constant propagation) wird die Variante mit dem "if" 
plötzlich zu
1
(insn 10 8 11 4 (set (reg:QI 43 [ D.1393 ])
2
        (mem/v:QI (const_int 193 [0xc1]) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])) test2.c:19 28 {movqi_insn}
3
     (nil))
4
5
(insn 11 10 13 4 (set (reg:QI 44 [ D.1394 ])
6
        (ior:QI (reg:QI 43 [ D.1393 ])
7
            (const_int 8 [0x8]))) test2.c:19 178 {iorqi3}
8
     (expr_list:REG_DEAD (reg:QI 43 [ D.1393 ])
9
        (nil)))
10
11
(insn 13 11 16 4 (set (mem/v:QI (const_int 193 [0xc1]) [0 MEM[(volatile uint8_t *)193B]+0 S1 A8])
12
        (reg:QI 44 [ D.1394 ])) test2.c:19 28 {movqi_insn}
13
     (expr_list:REG_DEAD (reg/f:HI 45)
14
        (expr_list:REG_DEAD (reg:QI 44 [ D.1394 ])
15
            (nil))))
was ja eigentlich "besser" und "hübscher" ist.

ich verstehe zwar nicht was das mit "constant propagation" zu tun hat, 
aber noch viel weniger verstehe ich warum diese Änderung/Verbesserung im 
"einfacheren" Fall ohne if() nicht durchgeführt wird...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael Reinelt schrieb:
> der "komplizierte" Code ist zwar gleich lang (5 Wörter) braucht aber
> einen takt länger. oder hab ich mich verzählt?

Braucht LD Rd,Z nicht 2 Zyklen?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Michael Reinelt schrieb:
>> der "komplizierte" Code ist zwar gleich lang (5 Wörter) braucht aber
>> einen takt länger. oder hab ich mich verzählt?
>
> Braucht LD Rd,Z nicht 2 Zyklen?

Wenn ich das Datenblatt richtig lese, braucht die "einfache" Variante 
ohne Inkrement/Dekrement des Z-Pointers nur einen Takt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael Reinelt schrieb:
> Wenn ich das Datenblatt richtig lese, braucht die "einfache" Variante
> ohne Inkrement/Dekrement des Z-Pointers nur einen Takt.

Von welchem COntroller reden wir?

Beim ATmega48 sind es 2 Zyklen, bei den neuen, halbierten AVRs 
(ATtiny4/5/9/10) kann es evtl. anders aussehen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Michael Reinelt schrieb:
>> Wenn ich das Datenblatt richtig lese, braucht die "einfache" Variante
>> ohne Inkrement/Dekrement des Z-Pointers nur einen Takt.
>
> Von welchem COntroller reden wir?
>
> Beim ATmega48 sind es 2 Zyklen, bei den neuen, halbierten AVRs
> (ATtiny4/5/9/10) kann es evtl. anders aussehen.

ATmega328P

Wo schaust du nach? ich hier: http://www.atmel.com/Images/doc0856.pdf 
Seite 93

Wobei beim XMEGA dabeisteht dass es bei Zugriff auf internes SRAM ein 
zyklus mehr ist, aber der 328 ist doch kein XMEGA, oder?

Und dann gibts da noch so Fußnoten...

Aber ich lese das so, dass das 1 zyklus braucht.

von m.n. (Gast)


Lesenswert?

Michael Reinelt schrieb:
> Wo schaust du nach? ich hier: http://www.atmel.com/Images/doc0856.pdf
> Seite 93

Schon lustig.
In dem Datenblatt von 9/2001 stehen da konstant 2 Zyklen.
Das müßte man mal genauestens nachmessen, oder gut sein lassen :-)

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


Lesenswert?

m.n. schrieb:
> Schon lustig. In dem Datenblatt von 9/2001 stehen da konstant 2 Zyklen.

Steht auch in älteren Versionen der instruction set summary.  Das
mit dem einen Zyklus trifft wirklich nur auf die tiny-Architektur
(ATtiny10 & Co.) zu bzw. auf die IO-Register vom Xmega.  Die Fußnoten
klingen auch verdammt nach „tiny“.  (Nur dort ist der Flash in den
normalen Adressbereich gemapt.)

Da hat man wohl den Abschnitt zuerst für den Xmega und dann für den
TinyTiny aufgebohrt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Nach der Tabelle auf Seite 13 im aktuellen Instruction-Set-Manual sind 
es ebenfalls 2 Zyklen für gewöhnliche AVRs und 1 Zyklus für Reduced- 
Core-TinyAVRs. Vielleicht wäre so langsam mal wieder ein neues Release 
dieses Dokuments fällig ;-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Ok, jetzt hab ich das "instruction Set Summary" im Datenblatt des 328P 
auch (zum erstenmal) gesehen, und da stehen auch 2 zyklen. Also werdens 
wohl wirklich zwei sein. Was den komplizierteren Code also nochmal 
langsamer macht...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael Reinelt schrieb:
> nach pass 161 (forward propagation) sieht der Code noch gleich aus,
> interessanterweise beide male noch mit "indirekter" Adressierung:
> [...]
>
> nach pass 162 (constant propagation) wird die Variante mit dem "if"
> plötzlich zu
>
1
> (insn 10 8 11 4 (set (reg:QI 43 [ D.1393 ])
2
>         (mem/v:QI (const_int 193 [0xc1]) [0 MEM[(volatile uint8_t 
3
> *)193B]+0 S1 A8])) test2.c:19 28 {movqi_insn}
4
>      (nil))
5
> ...
6
>
> was ja eigentlich "besser" und "hübscher" ist.
>
> ich verstehe zwar nicht was das mit "constant propagation" zu tun hat,

Propagiert wird die (compiletime) konstante Adresse 193, die nach 
Pseudo-Register r44 geladen wird. Pseudo r44 wird später ins Z-Register 
r30 allokiert. (Je nach Testfall unterscheidet sich die Anzahl der 
benötigten Pseudos und damit auch deren Numerierung.)

> aber noch viel weniger verstehe ich warum diese Änderung/Verbesserung im
> "einfacheren" Fall ohne if() nicht durchgeführt wird...

Hier ist dann Debuggen des Compilers angesagt und nachvollziehen der 
Quelle.  Im einfachsten Falle sind es fehlende / fehlerhafte 
Kostenbeschreibungen im avr-Backend.

IIRC werden Speicherzugriffe in neueren GCC-Versionen zunächst indirekt 
umgesetzt und erst in einem späteren Pass wieder in direkte Zugriffe 
gewandelt falls dies Vorteilhaft erscheint. Dies ermöglicht z.B. bessere 
Optimierung von Adressen die in Schleifen verwendet werden.

Pass "cprop" ist in gcc/cprop.c implementiert: 
http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/cprop.c?content-type=text%2Fplain&view=co

Vielleicht liefert -fdump-rtl-cprop-details o.ä. noch detailiertere 
Infos. Kosten können im avr-gcc mit -mlog=rtx_costs ausgegeben werden.

von m.n. (Gast)


Lesenswert?

Michael Reinelt schrieb:
> Was den komplizierteren Code also nochmal
> langsamer macht...

Freu Dich, dass Du den "komplizierten Code" nicht komplett in Assembler 
schreiben mußt.
Um einzelne Taktzyklen solltest Du Dich erst dann kümmern, wenn dieser 
Code >100000 Mal/s aufgerufen wird und Dein Controller schon mit 20MHz 
läuft.
Alles andere ist vergeudete Zeit.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

m.n. schrieb:
> Michael Reinelt schrieb:
>> Was den komplizierteren Code also nochmal
>> langsamer macht...
>
> Freu Dich, dass Du den "komplizierten Code" nicht komplett in Assembler
> schreiben mußt.
> Um einzelne Taktzyklen solltest Du Dich erst dann kümmern, wenn dieser
> Code >100000 Mal/s aufgerufen wird und Dein Controller schon mit 20MHz
> läuft.
> Alles andere ist vergeudete Zeit.

Gut dass nicht alle so denken. Open Source wie GCC lebt nun mal von 
engagierten Mitgliedern der Community, die das nicht als "vergeudete 
Zeit" ansehen, solche Eigenartigkeiten untersuchen und als Bug Report 
eintüten.

von m.n. (Gast)


Lesenswert?

Michael Reinelt schrieb:
> und als Bug Report eintüten.

Gartuliere, da hast Du ja einen richtig schweren Fehler gefunden.

Ich habe mit gerade einen Codeschnipsel in meinem Compilat angesehen. In 
einer ISR erfolgt:

PUSH R1
EOR R1,R1
.
.
POP R1

Zwischendurch wird R1 garnicht gebraucht. Ich könnte jetzt die 
verschwendeten Taktzyklen zählen, um zu dokumentieren welch schweren Bug 
ich gefunden habe.
Und, mache ich das? NEIN.

von (prx) A. K. (prx)


Lesenswert?

Ein Bug Report wäre da auch sinnlos. Der Compiler setzt ohne Kontrolle 
oder Ressourcenmanagement voraus, dass in R1 stets 0 drinsteht. Also 
muss der Anfang einer ISR diesen Zustand herstellen.

Und ja, eine verkehrt laufende Optimierung gehört wirklich nach 
Bugzilla. Dazu ist dieses Werkzeug ebenfalls da, auch wenns kein 
dramatischer Fall ist. Ob sich wer drum kümmern wird ist eine andere 
Sache, hohe Priorität hat das nicht.

Der Fall hat evtl. etwas bessere Chancen auf Beachtung, wenn man eine 
Vorversion findet, die es besser machte, und darauf explizit hinweist 
(regression).

von m.n. (Gast)


Lesenswert?

A. K. schrieb:
> hohe Priorität hat das nicht.

Was man meiner Meinung nach mit hoher Priorität angehen könnte, wären 
reservierte Register, die man in eigenen Ass.-Routinen sehr effizient 
verwenden kann.
IAR bietet das für R4-R15, was ISRs sehr schnell macht, weil im besten 
Fall kein PUSH/POP notwendig ist.

Ich sagte "könnte", denn ich kann das nicht und fordere auch nicht, dass 
ein Anderer dies für mich macht.

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


Lesenswert?

m.n. schrieb:
> Was man meiner Meinung nach mit hoher Priorität angehen könnte, wären
> reservierte Register, die man in eigenen Ass.-Routinen sehr effizient
> verwenden kann.

Gibt's doch:
1
uint8_t i __asm__("r2");

Musst du halt konsistent in deinem gesamten Projekt durchziehen, und
du musst dich bei den benutzten Bibliotheksroutinen vergewissern, dass
diese derartige Register nicht benutzen.

Es gibt aber keinen Grund, nun allen Nutzern diese Register zu
verbieten, nur weil hin und wieder mal einzelne sowas brauchen.
Daher halt auch die Forderung an denjenigen, der sowas macht, dass
er sich selbst um die Bibliothek kümmern möge, denn die
default-Bibliothek stellt sich das nicht zur Aufgabe.  (Man kann sich
aber natürlich auch eine eigene avr-libc compilieren, in der man
konsistent obiges Statement mit einbringt und auf diese Weise das
entsprechende Register im Compiler „reserviert“.

von Stefan E. (sternst)


Lesenswert?

Jörg Wunsch schrieb:
> Man kann sich
> aber natürlich auch eine eigene avr-libc compilieren, in der man
> konsistent obiges Statement mit einbringt und auf diese Weise das
> entsprechende Register im Compiler „reserviert“.

Muss man das (obiges Statement einbringen) überhaupt?
Sollte es nicht reichen, beim Kompilieren (der Lib) einfach -ffixed-2 zu 
benutzen?

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


Lesenswert?

Stefan Ernst schrieb:
> Sollte es nicht reichen, beim Kompilieren (der Lib) einfach -ffixed-2 zu
> benutzen?

Könnte sein.  Ich habe diese Option vorhin in der Doku gesucht, aber
nicht finden können.

von Stefan E. (sternst)


Lesenswert?

Jörg Wunsch schrieb:
> Ich habe diese Option vorhin in der Doku gesucht, aber
> nicht finden können.

http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html

von (prx) A. K. (prx)


Lesenswert?

m.n. schrieb:
> IAR bietet das für R4-R15, was ISRs sehr schnell macht, weil im besten
> Fall kein PUSH/POP notwendig ist.

Nur hat IAR ziemlich sicher keine 64-Bit Integers. Denn wenn im 
verbleibenden Registersatz keine 2 Operanden Platz finden, dann wirds 
schwierig.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

So, ich geb vorerst mal auf... (genug RTL gerlernt für die nächsten par 
Jahre :-)

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58322

von m.n. (Gast)


Lesenswert?

A. K. schrieb:
> Nur hat IAR ziemlich sicher keine 64-Bit Integers.

Doch, signed und unsigned. Und selbst, wenn man sich alle freien 
Register reserviert, geht noch 64-bit double.
Die Variablen werden wohl auf einem separaten CSTACK (Y) abgelegt, 
wodurch die Routinen auch noch unterbrechbar werden. Die LDD und STD 
Befehle sind dafür gut geeignet. Die Rücksprungadressen liegen auf dem 
RSTACK, den der AVR von Hause aus für CALL und RET bietet.
Ohne Frage hat das auch alles seinen Preis, weshalb ich Kritik am GCC 
wegen eines Taktzyklus etwas übertrieben finde.

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


Lesenswert?

m.n. schrieb:
> weshalb ich Kritik am GCC wegen eines Taktzyklus etwas übertrieben
> finde.

Darum geht's doch nicht, sondern einfach nur darum, dass es nicht
wirklich nachvollziehbar ist, warum der Compiler es mal so, mal so
entscheidet.

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.