Codeblähungen beim Rechnen mit globaler Variable

Gast #4366321
Lesenswert?

Hallo,

ich verwende avr-gcc zur Kompilierung für einen Atmega.

Ich habe jetzt schon mehrfach beobachtet, dass es den Code aufbläht, 
wenn man mit globalen Variablen rechnet, anstatt mit temporären, lokalen 
Variablen, die man dann am Ende der globalen Variable zuweist.

Warum ist das so? Sollte der Compiler nicht eigentlich sowas automatisch 
optimieren? Ich finde, es macht die Sache auch immer etwas 
unübersichtlich.
Wenn die globale Variable volatile wäre, wäre es das ja einleuchtend. 
Ist sie aber in dem Fall gar nicht mal.
#4366470
Lesenswert?

Jörg W. schrieb:
> Naja, das solltest du schon näher erläutern.

Z.B. hier sind die 2 RJMP überflüssig:
1
void foo( uint8_t val )
2
{
3
  uint8_t i = 45;
4
  if( val & 0x08 )
5
  44:  83 ff         sbrs  r24, 3
6
  46:  02 c0         rjmp  .+4        ; 0x4c <foo+0x8>
7
  48:  8b e7         ldi  r24, 0x7B  ; 123
8
  4a:  01 c0         rjmp  .+2        ; 0x4e <foo+0xa>
9
  4c:  8d e2         ldi  r24, 0x2D  ; 45
10
    i = 123;
11
  PORTB = i;
12
  4e:  88 bb         out  0x18, r24  ; 24
13
}
14
  50:  08 95         ret
Moderator Persönliche Seite #4366488
Lesenswert?

Peter D. schrieb:
> Z.B. hier sind die 2 RJMP überflüssig:

Hat aber nichts mit einer globalen Variablen zu tun.

Die Ursachen, warum beim Zugriff auf eine globale Variable der Code
umständlicher wird im Vergleich zur lokalen, beschreibt die erste
Antwort recht gut.  Wenn man's genauer haben will, müsste man den Code
im Detail sehen.

(Das soll nicht heißen, dass deine Bemerkung falsch wäre, aber die
müssten wir in einem separaten Thread diskutieren.)
#4366545
Lesenswert?

Das lag mir natürlich auf der Zunge, denn darauf kommt ein 
Asm-Programmierer nicht so leicht. Ich liess es aber lieber weg bevor 
der Zirkus hier auch noch losgeht. Ist auch nicht wirklich optimal, auf 
Basis von CMOV gehts besser.

Das Problem bei AVR (und auch Thumb, nicht aber ARM/Thumb2) liegt evtl. 
darin, dass der Compiler die Initialisierung wegschiebt und auf einen 
conditional select hinarbeitet, dann aber keinen hat. Also auf
  r = (cond) ? in1 : in2;
statt
  r = in1;
  if (cond) r = in2;

Nimmt man statt "45" einen Parameter, dann ist der Spuk weg und auch AVR 
machts richtig.
#4366580
Lesenswert?

Jörg W. schrieb:
> Hat aber nichts mit einer globalen Variablen zu tun.

Sollte auch nur ein Beispiel dafür sein, daß der AVR-GCC manchmal recht 
eigensinnig ist.

Wenn man sich über Code ärgert, stellt man ihn um und hebt ihn selten 
auf. Daher hab ich kein Beispiel griffbereit. Es ist mir aber 
aufgefallen.

Mir ist auch mal das Gegenteil passiert, daß er 3 globale Variablen 
unnötig in Register geladen hat, obwohl ein switch/case nur je eine 
davon geändert hat. Dadurch gingen ihm aber Register verloren, so daß er 
für lokale Variablen extra einen Stackframe anlegen mußte. Auch da habe 
ich den fetten Code leider nicht aufgehoben.

Auch bei Arrayzugriffen auf Structs berechnet er gerne die Basisadresse 
immer wieder neu. Erst mit einem Hilfspointer kann man ihn überreden, 
direkt mit Displacement auf das Element zuzugreifen.
Im Scheduler Beispiel benutze ich das (-> Operator).
Ich würde mir aber wünschen, daß ein gut optimierender Compiler auch 
beim . Operator einen gleich kompakten Code erzeugt, d.h. von sich aus 
einen Basepointer anlegt.

Ich will mich hier aber nicht beklagen, für einen freien Compiler ist 
die Codedichte ja ausreichend.
Vom IAR würde ich aber mehr erwarten.
Gast #4366693
Lesenswert?

Konkretes Beispiel.
Ich brauche zur Kontrolle im Programm eine ganz simple Checksumme über 
den Flash. Die soll in einer globalen Variable stehen und wird einfach 
beim Start generiert.

1
Checksum = 0;
2
uint16_t j;
3
for(j = 0; j < (uint16_t)&__data_load_end; j++) Checksum += pgm_read_byte(j);
4
if (boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS) != 0b11110111) Checksum += 1;
5
if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) != 0b11011100) Checksum += 2;
6
if (boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS) != 0b11111101) Checksum += 4;
7
if (boot_lock_fuse_bits_get(GET_LOCK_BITS) != 0b11101100) Checksum += 8;

Wenn ich es so schreibe, wird es kleiner:
1
uint8_t chk = 0;
2
uint16_t j;
3
for(j = 0; j < (uint16_t)&__data_load_end; j++) chk += pgm_read_byte(j);
4
if (boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS) != 0b11110111) chk += 1;
5
if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) != 0b11011100) chk += 2;
6
if (boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS) != 0b11111101) chk += 4;
7
if (boot_lock_fuse_bits_get(GET_LOCK_BITS) != 0b11101100) chk += 8;
8
Checksum = chk;

Das ist, wie gesagt, ein Beispiel. Das Phänomen tritt fast immer auf. 
Sogar mit statischen Variablen.
Gast #4366732
Lesenswert?

> Konkretes Beispiel. [...]

Das liegt daran, dass zwischen den Zugriffen auf die globale Variable 
andere Funktionen aufgerufen werden.  Diese könnten den Inhalt der 
globalen Variable ändern, deshalb muss der Compiler den Inhalt jedesmal 
neu lesen.  Bei lokalen Variablen weiss der Compiler, dass 
Unterfunktionen die lokale Variable nicht ändern können und er kann die 
Zugriffe schön optimieren.
Persönliche Seite #4366923
Lesenswert?

Peter D. schrieb:
> Z.B. hier sind die 2 RJMP überflüssig:

Hauptproblem sind Kostenbeschreibung im avr-gcc.  Dein Beispiel erzeugt 
mit -mbranch-cost=2 kleineren Code.  Allerdings erhalte ich mit dem 
Default (0) durchaus kleineren Code; im letzten Projekt sind das rund 2% 
(bei ca. 14k Binärcode).

Die Kosten zu überarbeiten wäre sehr zeitaufwändig und wird dadurch 
erschwert, dass es keine brauchbaren Benchmarks gibt.  Momentan haben 
Konstanten Kosten 0, was noch Denis stammt:

http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr.c?revision=230016&view=markup#l9902

A. K. schrieb:
> Wer mal sehen will, auf welche skurrilen Lösungen Compiler kommen
> können, der sollte sich mal das Resultat für amd64 ansehen:

Find ich naheliegend und nicht skurril :-)  Ein Flag wird zu 0 resp. -1 
gewandelt und dies als Maske, Summand, o.ä. verwendet.  avr-gcc macht 
das z.B. bei
1
#include <stdfix.h>
2

3
fract addss (fract a, sat fract b)
4
{
5
    return a - b;
6
}
1
addss:
2
  sub  r24,r22
3
  sbc  r25,r23
4
  brvc 0f
5
  ldi  r25,0x7f
6
  cp   r25,r23
7
  sbc  r24,r24
8
  sbci r25,-1
9
0:
10
  ret

A. K. schrieb:
> Ist auch nicht wirklich optimal, auf Basis von CMOV gehts besser.

IIRC hatte ich damit mal rumgespielt, aber das wurde dann ein Fass ohne 
Boden...  Aber nett wär's schon, z.B für Rx unsigned:
1
Ry = Rx < const1 ? -1 : const2;
1
cpi  Rx, const1
2
sbc  Ry, Ry
3
ori  Ry, const2
===
1
Ry = Rx < const1 ? const2 : 0;
1
cpi  Rx, const1
2
sbc  Ry, Ry
3
andi Ry, const2
===
1
if (Rx < const1)
2
    Ry++;
1
cpi  Rx, const1
2
adc  Ry, __zero_reg__
===
1
if (Rx >= const1)
2
    Ry++;
1
cpi  Rx, const1
2
sbci Ry, -1

Wobei anstatt const sinngemäß auch ein Register stehen kann.
#4366928
Lesenswert?

Johann L. schrieb:
> Find ich naheliegend und nicht skurril :-)

Du meinst, Asm-Experten wie Mobi verwenden routinemässig solchen Code?

Direkt selbsterkärend ist er jedenfalls nicht. Aus solchem Code 
rückwärts die eigentliche Operation rauszuporkeln ist Schwerstarbeit.

Vor langer Zeit bin ich mal dem GNU Superoptimizer (oder so ähnlich) 
begegnet, der alle Befehlssequenzen nacheinander durchzuprobieren 
scheint, um die Beste für eine kurze Operation zu finden.
Moderator Persönliche Seite #4367049
Lesenswert?

Danke!

Ja, es sind in der Tat die Inline-Assembler-Aufrufe, die offensichtlich
auf globale Variable ein Reload erzwingen.  Möglicherweise müssen die
zwar nicht "asm volatile" sein (wie sie es derzeit sind), aber auch
ohne das "volatile" ergibt sich der gleiche Code.  Ich hätte erwartet,
dass der Inline Assembler nur dann das Reload triggert, wenn man
explizit "memory" in der Clobber-Liste angibt.

Vielleicht hat Johann ja eine Idee.

p.s.: Habe auch mal das Umschreiben in eine static inline-Funktion
ausprobiert, die mit den Attributen "const" und "pure" deklariert
ist.  Hat leider auch keine Veränderung gebracht.
Gast #4367055
Lesenswert?

Mich würde das wirklich freuen, weil es sich in größeren Projekten schon 
läppert, was da an Codeverhau zusammenkommt. Alleine das kurze Stück 
macht schon 30 Bytes aus. Das Ganze x-mal, da kommt so einiges zusammen!
Das Reinbasteln von temporären Variablen, teilweise mehreren parallel 
ist halt sehr unschön. Und das Ganze passiert im Grunde unter allen 
gcc-Version, die ich getestet habe inkl. der 5er.
Moderator Persönliche Seite #4367059
Lesenswert?

Kurt Schluss schrieb:
> Mich würde das wirklich freuen, weil es sich in größeren Projekten schon
> läppert, was da an Codeverhau zusammenkommt. Alleine das kurze Stück
> macht schon 30 Bytes aus.

Naja, aber siehe oben: das nächste Problem ist wieder ein ganz
anderes.  Diese Analyse trifft nur auf das soeben gepostete zu,
bei dem die Fuses gelesen werden sollen, denn das ist am Ende in
Inline Assembler implementiert.

Generell ist die Variante mit der lokalen "Cache"-Variablen natürlich
immer sinnvoll, denn die zeigt dem Compiler recht eindeutig an, dass es
keine globalen Seiteneffekte geben kann.
Gast #4367086
Lesenswert?

Das ist nur ein Beispiel. Dass da die Fuses gelesen werden, ist dem 
Zufall geschuldet. Der Effekt tritt, wie gesagt, auch an vielen anderen 
Stellen auf, wo mit globalen Variablen gerechnet wird.
Moderator Persönliche Seite #4367092
Lesenswert?

Ja, andere Ursachen wurden dir ja schon genannt: für eine externe
Funktion kann der Compiler (ohne weitere Deklarationen wie eben die
genannten Funktions-Attribute) nicht wissen, ob diese eine globale
Variable verändern oder nicht.  Das ist einer der Nachteile der Sprache
C.  Daher ist er dann gezwungen, den Fall anzunehmen, die aufgerufene
Funktion hätte ihn geändert, und muss den Wert neu laden.  (Peter,
probier's mit dem IAR, würde mich wundern, wenn das dort grundlegend
anders wäre.)

Das Cachen in einer lokalen Variablen ist dagegen immer eine Abhilfe,
denn dann ist für den Compiler völlig klar, dass die gerufene Funktion
deren Wert nicht geändert haben kann.
Persönliche Seite #4367142
Lesenswert?

Jörg W. schrieb:
> Hast du eine Idee, warum das asm-Statement auch ohne "memory"-Clobber
> immer ein Reload erzwingt?

Kann ich so nicht nachvollziehen.  Erst wenn in folgendem Code der 
Vergleich einkommentiert wird, gibt es zusätzliche globale Zugriffe
1
#include <avr/io.h>
2

3
static inline uint8_t f (void)
4
{
5
    uint8_t x;
6
    __asm __volatile__ ("; =%0,%=" : "=r" (x));
7
    return x;
8
}
9

10
extern uint8_t X;
11

12
void X_init (void)
13
{
14
    X = 0;
15
    X += f();
16
    //if (X != 10)
17
        X -= f();
18
    X *= f ();
19
}

Unabhängig davon finde ich eine getChecksum() besser als eine 
initChecksum().
Gast #4367313
Lesenswert?

Blah Blubb
und wieder gelöscht

Peter D. schrieb:
> Jörg W. schrieb:
>> Naja, das solltest du schon näher erläutern.
>
> Z.B. hier sind die 2 RJMP überflüssig:void foo( uint8_t val )
> {
>   uint8_t i = 45;
>   if( val & 0x08 )
>   44:  83 ff         sbrs  r24, 3
>   46:  02 c0         rjmp  .+4        ; 0x4c <foo+0x8>
>   48:  8b e7         ldi  r24, 0x7B  ; 123
>   4a:  01 c0         rjmp  .+2        ; 0x4e <foo+0xa>
>   4c:  8d e2         ldi  r24, 0x2D  ; 45
>     i = 123;
>   PORTB = i;
>   4e:  88 bb         out  0x18, r24  ; 24
> }
>   50:  08 95         ret

Woher weiß PeDa das? Ist bisher nicht veröffentlicht. Nur für Dich, 
Jörg!
Gast #4367360
Lesenswert?

Jörg W. schrieb:
> as ist richtig, aber seit 15:51 Uhr ist er es, und seitdem haben wir
> genau darüber diskutiert.

Nein, diskutiert wurde vorher schon (für einige) - und ich finde es 
nicht richtig, das andere ausgeschlossen wurden.

Ist aber egal, ich werde das bei Andreas vorbringen und die Antwort 
abwarten. Wenn er das für korrekt hält bin ich halt raus ...
Moderator Persönliche Seite #4367435
Lesenswert?

Johann L. schrieb:
>> Hast du eine Idee, warum das asm-Statement auch ohne "memory"-Clobber
>> immer ein Reload erzwingt?
>
> Kann ich so nicht nachvollziehen.

Schade auch.  Ich hatte ein bisschen drauf gehofft, dass du dir auf
die diversen Zwischenschritte, die der GCC im Debugmodus ausgibt,
einen Reim machen könntest.

Als Fazit für den TE bleibt meiner Meinung nach, dass es immer sinnvoll
ist, sowas in lokalen Variablen zwischenzuspeichern.
Persönliche Seite #4367529
Lesenswert?

Jörg W. schrieb:
> Johann L. schrieb:
>>> Hast du eine Idee, warum das asm-Statement auch ohne "memory"-Clobber
>>> immer ein Reload erzwingt?
>>
>> Kann ich so nicht nachvollziehen.
>
> Schade auch.  Ich hatte ein bisschen drauf gehofft, dass du dir auf
> die diversen Zwischenschritte, die der GCC im Debugmodus ausgibt,
> einen Reim machen könntest.

Momentan hab ich keinen Nerv, darin rumzuwühlen.  Jedenfalls hats's nix 
mit asm und nix mit den volatiles und nix mit Aliasing zu tun, 
vergleiche den Code per
1
#include <avr/boot.h>
2

3
extern uint8_t Checksum;
4

5
void checksum_init2 (void)
6
{
7
  Checksum = 0;
8
  Checksum += 1 * (boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS) != 0b11110111);
9
  Checksum += 2 * (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) != 0b11011100);
10
  Checksum += 4 * (boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS) != 0b11111101);
11
  Checksum += 8 * (boot_lock_fuse_bits_get(GET_LOCK_BITS) != 0b11101100);
12
}

Wahrscheinlich irgendwo in PHI, PRE, FRE, CSE (nebst Target-Kosten) den 
Target-unabhängigen Passes.  Mit den Beispielen sollte zumindest der 
Pass, wo die Optimierung erwartet wird, auffindbar sein.

Z.B. per -fdump-tree-all-details
Gast #4370927
Lesenswert?

Hier nochmal ein weiteres Beispiel
1
#include <avr/io.h>
2

3
uint8_t Flags;
4

5
int __attribute__((OS_main)) main(void) {
6
  int16_t value = GPIOR1;
7

8
  #if 0
9

10
  static uint8_t A = 0;
11
  uint8_t tempA = A;
12
  if (value > 10) {
13
    if (tempA > 7) Flags |= 1;
14
    else tempA++;
15
  } else if (value < 5) {
16
    Flags &= ~1;
17
    tempA = 0;
18
  }
19
  A = tempA;
20

21
  #else
22

23
  static uint8_t A = 0;
24
  if (value > 10) {
25
    if (A > 7) Flags |= 1;
26
    else A++;
27
  } else if (value < 5) {
28
    Flags &= ~1;
29
    A = 0;
30
  }
31

32
  #endif
33

34
  GPIOR0 = Flags;
35

36
  while (1);
37
}

Ist direkt kompilierbar und ihr könnt #if 0 einfach durch #if 1 
ersetzen, um den (mir unverständlichen) Unterschied zu sehen.
#4370967
Lesenswert?

Kurt Schluss schrieb:
> ich verwende avr-gcc zur Kompilierung für einen Atmega.
>
> Ich habe jetzt schon mehrfach beobachtet, dass es den Code aufbläht,
> wenn man mit globalen Variablen rechnet, anstatt mit temporären, lokalen
> Variablen, die man dann am Ende der globalen Variable zuweist.
>
> Warum ist das so? Sollte der Compiler nicht eigentlich sowas automatisch
> optimieren? Ich finde, es macht die Sache auch immer etwas
> unübersichtlich.
> Wenn die globale Variable volatile wäre, wäre es das ja einleuchtend.
> Ist sie aber in dem Fall gar nicht mal.

Globale Variablen sind igitt. Darum haben sich die Compilerentwickler 
keine Mühe gegeben, den Compiler für solchen Quark zu verbessern. Die 
haben genug damit zu tun, Optimierungen für guten Code zu entwickeln. 
;-)
Gast #4371011
Lesenswert?

Flags ist eine globale Variable, die dieses Mal nicht in einer 
temporären Variable zwischengespeichert wird! Mir ging es diesmal nur 
darum, zu zeigen, dass das selbe Verhalten auch bei static auftritt.

Aaaber natürlich könnte man auch Flags zwischenspeichern! Kannst es ja 
mal probieren. Bringt auch wieder einige Bytes. Leider wird der Code 
dann langsam aber sicher unleserlich... und genau das ist, was mir nicht 
gefällt.
Moderator Persönliche Seite #4371243
Lesenswert?

Kurt Schluss schrieb:
> Ist direkt kompilierbar und ihr könnt #if 0 einfach durch #if 1
> ersetzen, um den (mir unverständlichen) Unterschied zu sehen.

Hab ich mal gemacht - mit avr-gcc 4.7.2.

Ergebnis: Die Variante mit(!) temporärer Variable ist länger.

Mit temporärer Variable (#if 1):
1
avr-gcc  -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99 -da -flto     -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT test.o -MF dep/test.o.d  -c  ../test.c
2
avr-gcc -mmcu=atmega328p -flto  -Os -Wl,-Map=test.map test.o     -o test.elf
3
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  test.elf test.hex
4
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex test.elf test.eep || exit 0
5
avr-objdump -h -S test.elf > test.lss
6

7
AVR Memory Usage
8
----------------
9
Device: atmega328p
10

11
Program:     206 bytes (0.6% Full)
12
(.text + .data + .bootloader)
13

14
Data:          2 bytes (0.1% Full)
15
(.data + .bss + .noinit)
16

17
Build succeeded with 0 Warnings...

Ohne temporäre Variable (#if 0):
1
avr-gcc  -mmcu=atmega328p -Wall -gdwarf-2 -std=gnu99 -da -flto     -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT test.o -MF dep/test.o.d  -c  ../test.c
2
avr-gcc -mmcu=atmega328p -flto  -Os -Wl,-Map=test.map test.o     -o test.elf
3
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  test.elf test.hex
4
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex test.elf test.eep || exit 0
5
avr-objdump -h -S test.elf > test.lss
6

7
AVR Memory Usage
8
----------------
9
Device: atmega328p
10

11
Program:     196 bytes (0.6% Full)
12
(.text + .data + .bootloader)
13

14
Data:          2 bytes (0.1% Full)
15
(.data + .bss + .noinit)
16

17
Build succeeded with 0 Warnings...

Also wo ist das Problem? Oder anders gefragt: welche avr-gcc-Version 
verwendest Du?
Moderator Persönliche Seite #4371460
Lesenswert?

GCC 4.9.3:
1
$ diff -u notempvar.s  tempvar.s
2
--- notempvar.s 2015-12-01 11:44:04.342113974 +0100
3
+++ tempvar.s   2015-12-01 11:43:45.117960315 +0100
4
@@ -14,23 +14,28 @@
5
 .L__stack_usage = 0
6
        in r18,0x2a
7
        ldi r19,0
8
+       lds r25,A.1518
9
        lds r24,Flags
10
        cpi r18,11
11
        cpc r19,__zero_reg__
12
        brlt .L2
13
-       lds r25,A.1518
14
        cpi r25,lo8(8)
15
-       brlo .L4
16
+       brlo .L3
17
        ori r24,lo8(1)
18
-       rjmp .L6
19
+       sts Flags,r24
20
+       rjmp .L4
21
+.L3:
22
+       subi r25,lo8(-(1))
23
+       rjmp .L4
24
 .L2:
25
        cpi r18,5
26
        cpc r19,__zero_reg__
27
        brge .L4
28
        andi r24,lo8(-2)
29
-.L6:
30
        sts Flags,r24
31
+       ldi r25,0
32
 .L4:
33
+       sts A.1518,r25
34
        lds r24,Flags
35
        out 0x1e,r24
36
 .L5:
Gast #4371871
Lesenswert?

Hmmmmmmm. Hatte es auch mit der 4.9.3 getestet, bzw. auch damit zum 
Minimalbeispiel reduziert. Dass es bei anderen Versionen gegensätzliche 
Ergebnisse gibt, ist jetzt nicht gerade vertrauenserweckend. :/

Ich sehe aber gerade, dass mein Beispiel auch zum Teil Käse ist: Der 
Zähler A kann bei nur einem Durchlauf (wie im reduzierten 
Minimalbeispiel) nie über 1 hinaus kommen. Da ist jetzt die Frage, wie 
schlau der Compiler ist und was er wegoptimiert. Normal müsste man hier 
alles in ein while (1) {...} packen.

Im realen Programm macht es jedenfalls so einige Bytes aus, mit vs. ohne 
tempA.
#4371883
Lesenswert?

Kurt Schluss schrieb:
> Dass es bei anderen Versionen gegensätzliche
> Ergebnisse gibt, ist jetzt nicht gerade vertrauenserweckend. :/

Aber auch nicht weiter ungewöhnlich. Der Optimizer des GCC besteht aus 
vielen Teilen und die meisten sind nicht speziell auf eine Zielmaschine 
abgestimmt. Die werden deshalb auch von etlichen verschiedenen Leuten 
gepflegt. Es kann folglich vorkommen, dass Änderungen an im Prinzip 
maschinenunabhängigen Optimierungen bei manchen Zielmaschinen nach 
hinten losgehen.
Moderator Persönliche Seite #4371893
Lesenswert?

Kurt Schluss schrieb:
> Hmmmmmmm. Hatte es auch mit der 4.9.3 getestet, bzw. auch damit zum
> Minimalbeispiel reduziert. Dass es bei anderen Versionen gegensätzliche
> Ergebnisse gibt, ist jetzt nicht gerade vertrauenserweckend. :/

Warum? Da gibt es noch diverse Knöpfchen. Fängt bei der Optimierung an:

   -O, -O2, -O3, -Os

> Im realen Programm macht es jedenfalls so einige Bytes aus, mit vs. ohne
> tempA.

Bei statics, die lokal in der Funktion definiert werden? Glaube ich 
nicht.
Gast #4401063
Lesenswert?

A. K. schrieb:
> Wer mal sehen will, auf welche skurrilen Lösungen Compiler kommen
> können, der sollte sich mal das Resultat für amd64 ansehen:
>         andl    $8, %edi
>         cmpb    $1, %dil
>         sbbl    %eax, %eax
>         andl    $-78, %eax
>         addl    $123, %eax
>         movb    %al, PORTB(%rip)

nennt sich branchless condition, guter code

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren