Forum: Compiler & IDEs avr-gcc 4.7 I/O-Zugriffe


von Lars R. (larsr)


Lesenswert?

Hallo,

Ich beobachte schon seit längerer Zeit die Entwicklung des avr-gcc und 
probiere auch immer mal wieder zwischendrin die aktuellen 
Entwicklungsstände aus.

Momentan gibt es aber mit dem 4.7 irgendwie Schwierigkeiten mit den 
I/O-Zugriffen. Das geschieht erst seit Kurzem.

Aus dem Code:
1
    unsigned char n = 0x00;
2
    if (PINA & (1 << 5))
3
        n = 0x03;
4
    if (PINA & (1 << 4))
5
        n ^= 0x01;
6
    m_LastState = n;

Wird:
1
    6d74:  89 b3         in  r24, 0x19  ; 25
2
    6d76:  85 fd         sbrc  r24, 5
3
    6d78:  02 c0         rjmp  .+4        ; 0x6d7e <main+0x4c>
4
    6d7a:  80 e0         ldi  r24, 0x00  ; 0
5
    6d7c:  01 c0         rjmp  .+2        ; 0x6d80 <main+0x4e>
6
    6d7e:  83 e0         ldi  r24, 0x03  ; 3
7
    6d80:  99 b3         in  r25, 0x19  ; 25
8
    6d82:  94 ff         sbrs  r25, 4
9
    6d84:  02 c0         rjmp  .+4        ; 0x6d8a <main+0x58>
10
    6d86:  91 e0         ldi  r25, 0x01  ; 1
11
    6d88:  89 27         eor  r24, r25
12
    6d8a:  80 93 5a 06   sts  0x065A, r24

SBIC/SBIS wird nicht mehr erzeugt, stattdessen werden lokale Kopien 
erzeugt, welche aber durch das "volatile" immer wieder verworfen werden 
und nicht wiederverwertet werden. Weiterhin sind diese RJMP-Konstrukte 
sehr auffällig. Optimierungsstufe ist -Os, der Code ist der aktuelle aus 
dem SVN.

Kann damit jemand etwas anfangen? Ich weiß, dass hier GCC-Experten 
mitlesen, daher denke ich, dass das Thema hier gut aufgehoben ist.

Es gibt noch mehr Verschlechterungen seit GCC 4.6, meistens ebenfalls im 
Zusammenhang mit bedingten Sprüngen. Stand nicht auch mal irgendwo, dass 
der avr-gcc keine Sprunganweisungen über JMP-Befehlen erzeugt? Da gab es 
doch mal einen Hardwarefehler in den AVR, oder?


Viele Grüße,

Lars

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:

> SBIC/SBIS wird nicht mehr erzeugt, stattdessen werden lokale Kopien
> erzeugt, welche aber durch das "volatile" immer wieder verworfen werden
> und nicht wiederverwertet werden. Weiterhin sind diese RJMP-Konstrukte
> sehr auffällig. Optimierungsstufe ist -Os, der Code ist der aktuelle aus
> dem SVN.

Ist PR51425; der ist mir erst kürzlich im Zusammenhang mit PR51374 
aufgefallen. Wie's aussieht ist er trivial zu beheben.

Dummerweise gibt es in der GCC-Testsuite keinen einzigen Testfall, der 
I/O oder ISRs abdeckt. Tetsprogramme fallen eben nicht vom Himmel...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:

> Es gibt noch mehr Verschlechterungen seit GCC 4.6, meistens ebenfalls im
> Zusammenhang mit bedingten Sprüngen.

Hast du Beispiele?

> Stand nicht auch mal irgendwo, dass der avr-gcc keine
> Sprunganweisungen über JMP-Befehlen erzeugt?

Doch, macht er schon. Zum Beisiel mit
1
#define NOP0 asm volatile ("nop":)
2
#define NOP1 NOP0;NOP0;NOP0;NOP0;NOP0;NOP0;NOP0;NOP0;NOP0;NOP0
3
#define NOP2 NOP1;NOP1;NOP1;NOP1;NOP1;NOP1;NOP1;NOP1;NOP1;NOP1
4
#define NOP3 NOP2;NOP2;NOP2;NOP2;NOP2;NOP2;NOP2;NOP2;NOP2;NOP2
5
#define NOP4 NOP3;NOP3;NOP3;NOP3;NOP3
6
7
#define PORTB (*((unsigned char volatile*) 0x38))
8
9
extern char c;
10
11
void nops (void)
12
{ 
13
    if (PORTB & 0x80)
14
    {
15
        NOP4;
16
    }
17
}

Oder mit folgendem Code:
1
extern int bar (int);
2
3
int foo (int x)
4
{ 
5
    return bar (x+bar(x));
6
}

Der übersetzt wird zu
1
foo:
2
  push r28   ;  22  pushqi1/1  [length = 1]
3
  push r29   ;  23  pushqi1/1  [length = 1]
4
  movw r28,r24   ;  2  *movhi/1  [length = 1]
5
  call bar   ;  7  *call_value_insn/2  [length = 2]
6
  add r24,r28   ;  10  *addhi3/1  [length = 2]
7
  adc r25,r29
8
  pop r29   ;  25  popqi  [length = 1]
9
  pop r28   ;  26  popqi  [length = 1]
10
  jmp bar   ;  11  *call_value_insn/4  [length = 2]



> Da gab es doch mal einen Hardwarefehler in den AVR, oder?

Welcher denn? Ist mir nix bekannt in der Richtung ausser dem "Skip-Bug" 
beim Skip von 4-Byre Instruktionen.

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Ist PR51425; der ist mir erst kürzlich im Zusammenhang mit PR51374
> aufgefallen. Wie's aussieht ist er trivial zu beheben.
>
> Dummerweise gibt es in der GCC-Testsuite keinen einzigen Testfall, der
> I/O oder ISRs abdeckt. Tetsprogramme fallen eben nicht vom Himmel...

Nun ja, mit dem I/O alleine ist es ja nicht getan. Der Unsinn mit den 
ganzen RJMP müsste auch noch korrigiert werden. Ich würde ja erwarten, 
dass der Code
1
unsigned char n = 0x00;
2
if (PINA & (1 << 5))
3
    n = 0x03;

als
1
clr r24
2
sbic PINA,5
3
ldi r24,0x03

übersetzt würde. Da bräuchte ich gar kein RJMP. Also eigentlich zwei 
verschiedene Problemfälle in einem dreizeiligen Stück Code.

Johann L. schrieb:
> Hast du Beispiele?

Ja, natürlich.

Der C-Code:
1
// Sendet nur das untere Nibble eines Bytes an das Display.
2
void SendLowNibble(const unsigned char byte)
3
{
4
    PORTA &= ~(1 << D_CLK);   // Takt L
5
    PORTA &= ~(1 << D_DI);    // D = 0
6
    if (byte & 0x01)
7
        PORTA |= (1 << D_DI); // D = 1
8
    PORTA |= (1 << D_CLK);    // Takt H
9
    PORTA &= ~(1 << D_CLK);   // Takt L
10
    PORTA &= ~(1 << D_DI);    // D = 0
11
    if (byte & 0x02)
12
        PORTA |= (1 << D_DI); // D = 1
13
    PORTA |= (1 << D_CLK);    // Takt H
14
    PORTA &= ~(1 << D_CLK);   // Takt L
15
    PORTA &= ~(1 << D_DI);    // D = 0
16
    if (byte & 0x04)
17
        PORTA |= (1 << D_DI); // D = 1
18
    PORTA |= (1 << D_CLK);    // Takt H
19
    PORTA &= ~(1 << D_CLK);   // Takt L
20
    PORTA &= ~(1 << D_DI);    // D = 0
21
    if (byte & 0x08)
22
        PORTA |= (1 << D_DI); // D = 1
23
    PORTA |= (1 << D_CLK);    // Takt H
24
}

Erzeugt folgenden ASM:
1
     c2e:  d9 98         cbi  0x1b, 1  ; 27
2
     c30:  d8 98         cbi  0x1b, 0  ; 27
3
     c32:  80 fd         sbrc  r24, 0
4
     c34:  d8 9a         sbi  0x1b, 0  ; 27
5
     c36:  d9 9a         sbi  0x1b, 1  ; 27
6
     c38:  d9 98         cbi  0x1b, 1  ; 27
7
     c3a:  d8 98         cbi  0x1b, 0  ; 27
8
     c3c:  81 fd         sbrc  r24, 1
9
     c3e:  d8 9a         sbi  0x1b, 0  ; 27
10
     c40:  d9 9a         sbi  0x1b, 1  ; 27
11
     c42:  d9 98         cbi  0x1b, 1  ; 27
12
     c44:  d8 98         cbi  0x1b, 0  ; 27
13
     c46:  82 fd         sbrc  r24, 2
14
     c48:  d8 9a         sbi  0x1b, 0  ; 27
15
     c4a:  d9 9a         sbi  0x1b, 1  ; 27
16
     c4c:  d9 98         cbi  0x1b, 1  ; 27
17
     c4e:  d8 98         cbi  0x1b, 0  ; 27
18
     c50:  83 fd         sbrc  r24, 3
19
     c52:  d8 9a         sbi  0x1b, 0  ; 27
20
     c54:  d9 9a         sbi  0x1b, 1  ; 27
21
     c56:  08 95         ret

Besser könnte ich es selbst auch nicht. ;-)

Aber der C-Code:
1
// Sendet nur das obere Nibble eines Bytes an das Display.
2
void SendHighNibble(const unsigned char byte)
3
{
4
    PORTA &= ~(1 << D_CLK);   // Takt L
5
    PORTA &= ~(1 << D_DI);    // D = 0
6
    if (byte & 0x10)
7
        PORTA |= (1 << D_DI); // D = 1
8
    PORTA |= (1 << D_CLK);    // Takt H
9
    PORTA &= ~(1 << D_CLK);   // Takt L
10
    PORTA &= ~(1 << D_DI);    // D = 0
11
    if (byte & 0x20)
12
        PORTA |= (1 << D_DI); // D = 1
13
    PORTA |= (1 << D_CLK);    // Takt H
14
    PORTA &= ~(1 << D_CLK);   // Takt L
15
    PORTA &= ~(1 << D_DI);    // D = 0
16
    if (byte & 0x40)
17
        PORTA |= (1 << D_DI); // D = 1
18
    PORTA |= (1 << D_CLK);    // Takt H
19
    PORTA &= ~(1 << D_CLK);   // Takt L
20
    PORTA &= ~(1 << D_DI);    // D = 0
21
    if (byte & 0x80)
22
        PORTA |= (1 << D_DI); // D = 1
23
    PORTA |= (1 << D_CLK);    // Takt H
24
}

Erzeugt wieder Unsinn:
1
     c02:  d9 98         cbi  0x1b, 1  ; 27
2
     c04:  d8 98         cbi  0x1b, 0  ; 27
3
     c06:  98 2f         mov  r25, r24
4
     c08:  84 fd         sbrc  r24, 4
5
     c0a:  d8 9a         sbi  0x1b, 0  ; 27
6
     c0c:  d9 9a         sbi  0x1b, 1  ; 27
7
     c0e:  d9 98         cbi  0x1b, 1  ; 27
8
     c10:  d8 98         cbi  0x1b, 0  ; 27
9
     c12:  95 fd         sbrc  r25, 5
10
     c14:  d8 9a         sbi  0x1b, 0  ; 27
11
     c16:  d9 9a         sbi  0x1b, 1  ; 27
12
     c18:  d9 98         cbi  0x1b, 1  ; 27
13
     c1a:  d8 98         cbi  0x1b, 0  ; 27
14
     c1c:  96 fd         sbrc  r25, 6
15
     c1e:  d8 9a         sbi  0x1b, 0  ; 27
16
     c20:  d9 9a         sbi  0x1b, 1  ; 27
17
     c22:  d9 98         cbi  0x1b, 1  ; 27
18
     c24:  d8 98         cbi  0x1b, 0  ; 27
19
     c26:  87 fd         sbrc  r24, 7
20
     c28:  d8 9a         sbi  0x1b, 0  ; 27
21
     c2a:  d9 9a         sbi  0x1b, 1  ; 27
22
     c2c:  08 95         ret

Der Code ist zwar korrekt, aber wieso werden die Bits 4 und 7 von r24 
gelesen und für die Bits 5 und 6 wird plötzlich r25 gebraucht?

Die beiden Funktionen sind absolut identisch, bis auf die Kleinigkeit, 
dass hier das obere Nibble genutzt wird.

Der C-Code:
1
// Ruft den aufsummierten Wert ab.
2
signed char GetEncoderDelta(void)
3
{
4
    unsigned char sreg;
5
    sreg = SREG;
6
    cli();
7
    signed char n = m_CurrentDelta;
8
    m_CurrentDelta &= 0x01;
9
    SREG = sreg;
10
    n >>= 1;
11
    return n;
12
}

Erzeugt dieses:
1
     f16:  2f b7         in  r18, 0x3f  ; 63
2
     f18:  f8 94         cli
3
     f1a:  e9 e5         ldi  r30, 0x59  ; 89
4
     f1c:  f6 e0         ldi  r31, 0x06  ; 6
5
     f1e:  80 81         ld  r24, Z
6
     f20:  90 81         ld  r25, Z
7
     f22:  91 70         andi  r25, 0x01  ; 1
8
     f24:  90 93 59 06   sts  0x0659, r25
9
     f28:  2f bf         out  0x3f, r18  ; 63
10
     f2a:  85 95         asr  r24
11
     f2c:  08 95         ret

Wieso wird hier der Z-Pointer zurechtgebaut, wenn es auch ein LDS und 
ein MOV täte. Zum Speichern wird dann noch nicht einmal der Pointer 
genutzt. Hier wieder nur ein STS. Die Kostenrechnung scheint also nicht 
zu funktionieren. Zwei LDI und zwei LD sind doch nicht billiger als ein 
LDS...

GCC 4.6 hat dies noch nicht gemacht!

Wenn wenigstens dann auch der Z-Pointer mit ST zum Speichern genutzt 
würde. Der GCC scheint dies aber zu vergessen, dass hier bereits der 
richtige Wert drinsteht. Denn ein ST mit zwei Bytes wäre doch günstiger 
als ein STS mit vier Bytes.

Johann L. schrieb:
> Welcher denn? Ist mir nix bekannt in der Richtung ausser dem "Skip-Bug"
> beim Skip von 4-Byre Instruktionen.

Genau den meinte ich. Wenn es so ist, wie du schreibst, dann ist ja 
alles gut.

Wenn der GCC nicht so ein schrecklich-überladenes Stück Software wäre, 
würde ich ja selbst einmal versuchen, Hand anzulegen. Aber es erscheint 
mir sehr schwierig hier etwas zu "verbessern" ohne wo anders neue Gräben 
aufzureißen...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Ist PR51425; der ist mir erst kürzlich im Zusammenhang mit PR51374
>> aufgefallen. Wie's aussieht ist er trivial zu beheben.
>>
>> Dummerweise gibt es in der GCC-Testsuite keinen einzigen Testfall, der
>> I/O oder ISRs abdeckt. Tetsprogramme fallen eben nicht vom Himmel...
>
> Nun ja, mit dem I/O alleine ist es ja nicht getan. Der Unsinn mit den
> ganzen RJMP müsste auch noch korrigiert werden. Ich würde ja erwarten,
> dass der Code
>
> übersetzt würde. Da bräuchte ich gar kein RJMP. Also eigentlich zwei
> verschiedene Problemfälle in einem dreizeiligen Stück Code.

Wie man das behebt im GCC ist bekannt. Wenn du jetzt noch sagtst, wo die 
Zeit dafür herkommen soll...

> Johann L. schrieb:
>> Hast du Beispiele?
>
> Aber der C-Code:
>
> Erzeugt wieder Unsinn:
>

Naja "Unsinn...". Eine Instruktion zu viel.

Übrigens ist es eine deutliche Erleuterung, wenn Beispiele als 
übersetzbarer Code daherkommen, und man nicht noch ewig Deklarationen 
etc. nachflicken muss.

> Der C-Code:
>
> Erzeugt dieses:
>
> Wieso wird hier der Z-Pointer zurechtgebaut, wenn es auch ein LDS und
> ein MOV täte.

Weil es zu dem Zeitpunkt, wo die Entscheidung getroffen wird, weder MOV 
noch LDS gibt ;-)

Sieht nach PR50448 aus, für den es in trunk 181011 ein Patch gab.

Ist deine Version älter?

Auf den Code kann ick kein Bezug nehmen. Wenn ich es dennoch versuche, 
bekomme ich von der Forensoftware angemeckert:

"Bitte reduzieren Sie die Anzahl der Zitatzeilen."

WAS SOLL DAS DENN NUN SCHON WIEDER???

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Wie man das behebt im GCC ist bekannt. Wenn du jetzt noch sagtst, wo die
> Zeit dafür herkommen soll...

Achso, na dann ist ja gut. Ich hatte dich so verstanden, dass du weißt, 
wie man die I/O-Sache korrigiert. Dass du das auch auf die 
RJMP-Problematik mit bezogen hast, war mir nicht ersichtlich.

Tut mir Leid, ich habe aber für die Zeitfrage auch keine Lösung parat. 
Es ist nicht alles so einfach wie C im Leben. Ich bin dir ja aber schon 
dankbar, dass du überhaupt Zeit für den avr-gcc findest. Seit Version 
4.3 hat sich ja einiges getan. Version 4.7 verschafft mir beispielsweise 
1,6 Kilobytes mehr freien Speicher in einem Projekt, welches bei Version 
4.3 noch einen ATmega32 komplett ausgefüllt hatte. Ich finde das 
beachtlich!

Johann L. schrieb:
> Naja "Unsinn...". Eine Instruktion zu viel.

Es geht nicht um die eine Instruktion an sich. Es geht darum, wieso der 
GCC meint, dass das Bit 5 und 6 aus r25 kommen muss, wenn es auch r24 
für die Bits 0 bis 4 und 7 tut. Da dies nicht "sinnvoll" ist, sage ich 
halt frech, dass es "Unsinn" ist. Nimm's mir nicht übel. ;-)

Johann L. schrieb:
> Übrigens ist es eine deutliche Erleuterung, wenn Beispiele als
> übersetzbarer Code daherkommen, und man nicht noch ewig Deklarationen
> etc. nachflicken muss.

Was hat bei meinen Beispielen gefehlt? "m_CurrentDelta" und 
"m_LastState" haben gefehlt. Stimmt. Sorry:
1
volatile signed char m_CurrentDelta;
2
3
unsigned char m_LastState;

Johann L. schrieb:
> Weil es zu dem Zeitpunkt, wo die Entscheidung getroffen wird, weder MOV
> noch LDS gibt ;-)

Das kann ja aber nicht die Lösung sein. Wäre dies so, würde ja immer 
LDI, LDI, LDS im Code vorkommen. Dann könnte man ja auch argumentieren, 
der GCC wusste ja nicht, dass er ein LDS nutzen wollte, also hat er mal 
sicherheitshalber die beiden LDI ausgegeben.

Und wieso dann ein STS anstelle des ST ausgegeben wird, erklärt sich 
dadurch auch nicht. Die Entscheidung den Z-Pointer zu nutzen, wurde da 
ja schon längst getroffen.

Johann L. schrieb:
>
> Sieht nach PR50448 aus, für den es in trunk 181011 ein Patch gab.
>
> Ist deine Version älter?

Mein aktuelles "Testobjekt" entstammt der Revision 181773 aus dem 
offiziellen SVN. Das sind die letzten Stände des AVR-Backend. Was 
Neueres konnte ich noch nicht erblicken.

Das einzige, was ich an dem Code geändert habe, ist es, das Patch 
anzuwenden:

http://www.mail-archive.com/gcc@gcc.gnu.org/msg61130.html

Durch die Einführung dieser Address Spaces wurde vor etwa drei oder vier 
Wochen die Erzeugung des LTO-Frontends korrumpiert. LTO funktioniert mit 
dem Patch wieder. Übrigens funktionierte LTO seit der ersten Version 
(also GCC 4.5.0) an zuverlässig und korrekt, auch wenn im Internet 
Gegenteiliges zu lesen ist!

Die Binutils sind das aktuelle Release. Also nicht die Codes aus deren 
SVN.

Johann L. schrieb:
> Auf den Code kann ick kein Bezug nehmen. Wenn ich es dennoch versuche,
> bekomme ich von der Forensoftware angemeckert:
>
> "Bitte reduzieren Sie die Anzahl der Zitatzeilen."
>
> WAS SOLL DAS DENN NUN SCHON WIEDER???

Also dafür kann ich nun wirklich nichts... ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Wie man das behebt im GCC ist bekannt. Wenn du jetzt noch sagtst, wo die
>> Zeit dafür herkommen soll...
>
> Achso, na dann ist ja gut. Ich hatte dich so verstanden, dass du weißt,
> wie man die I/O-Sache korrigiert. Dass du das auch auf die
> RJMP-Problematik mit bezogen hast, war mir nicht ersichtlich.

Das Problem ist, daß, um überhaupt zu den Skip-Befehlen zu kommen, 
Befehle vereinbart werden müssen, die ein "conditional jump on bit" 
machen. Weil das Sprungziel irgendwo liegen kann, wird dieser 
(Pseudo)Befehl dann ausgegeben als Skip-über-RJMP.

Nur wenn genau eine Instruktion zu überspringen ist kann das RJMP 
wegfallen. Instruktionslängen sind erst sehr spät im Compiliervorgang 
bekannt jedoch nicht zu der Zeit, zu der "conditional jump on bit" 
erzeugt wird.

> Seit Version 4.3 hat sich ja einiges getan. Version 4.7 verschafft
> mir beispielsweise 1,6 Kilobytes mehr freien Speicher in einem
> Projekt, welches bei Version 4.3 noch einen ATmega32 komplett
> ausgefüllt hatte. Ich finde das beachtlich!

Mit Änderungen/Erweiterungen/Optimierungen im AVR-Teil dürfte das 
absolut garnix zu tun haben, weil es dort von 4.3 bis 4.6 
einschliesslich de facto überhaupt keine Ändrungen gab.

> Johann L. schrieb:
>> Naja "Unsinn...". Eine Instruktion zu viel.
>
> Es geht nicht um die eine Instruktion an sich. Es geht darum, wieso der
> GCC meint, dass das Bit 5 und 6 aus r25 kommen muss, wenn es auch r24
> für die Bits 0 bis 4 und 7 tut. Da dies nicht "sinnvoll" ist, sage ich
> halt frech, dass es "Unsinn" ist. Nimm's mir nicht übel. ;-)

Solche Artefakte zu debuggen und die Ursache zu finden, ist mir ehrlich 
gesagt zu aufwändig. Allein die Stelle zu finden dürfte mehrere Wochen 
dauerm. Das Problem sachgemäß zu beheben dauert idR nochmal so lange, 
und für eine Instruktion ist mir der Aufwand einfach zu hoch. Zudem 
geschieht es in dem Teil von GCC, der für alle Architekturen verwendet 
wird, also auch für 64-Bit Systeme etc. Und dort Regression-Tests und 
Benchmaks zu machen, ist definitiv jenseits von dem, was ich beitragen 
kann.

Das einzige, was vielleicht helfen könnte, ist einen aussagekräftigen 
Bugreport zu machen. Die Chance, daß er Beachtung findet, ist allerdings 
sehr klein, denn AVR ist eine "unwichtige" Plattform.

AVR ist "unwichtig", weil es nicht genug Entwickler gibt, die sich um 
den AVR-Teil kümmern. Zudem hat der AVR-Teil andere Einschränkungen; so 
ist er zB nicht standardkonform weil double = 32Bit etc.

> Johann L. schrieb:
>> Weil es zu dem Zeitpunkt, wo die Entscheidung getroffen wird, weder MOV
>> noch LDS gibt ;-)
>
> Das kann ja aber nicht die Lösung sein. Wäre dies so, würde ja immer
> LDI, LDI, LDS im Code vorkommen. Dann könnte man ja auch argumentieren,
> der GCC wusste ja nicht, dass er ein LDS nutzen wollte, also hat er mal
> sicherheitshalber die beiden LDI ausgegeben.

Die Gründe hier darzulegen führt mir zu weit. Vielleicht ist dein 
Interesse ja so hoch, daß du einen Blick unter die Motorhaube wirfst? In 
dem Fall wäre es dann auch möglich, sich auf einer technischen Ebene zu 
unterhalten anstatt nicht-zutreffende Bildchen verwenden zu müssen.

> Johann L. schrieb:
>>
>> Sieht nach PR50448 aus, für den es in trunk 181011 ein Patch gab.

Evtl. lannst di den PR wieder öffnen mit deinem Code, wenn es wirklich 
das gleiche Artefakt ist. Der Bug geht so: Für alle Speicherzugriffe 
werden indirekte Zugriffe erzeugt weil das idR günstiger ist und 
Adressen recyclet werden können. Nachfolgende Passes wie cprop fügen 
dann die Adresse ein, wenn sie bekannt ist und übersehen wohl Teile.

>> Ist deine Version älter?
>
> Mein aktuelles "Testobjekt" entstammt der Revision 181773 aus dem
> offiziellen SVN. Das sind die letzten Stände des AVR-Backend. Was
> Neueres konnte ich noch nicht erblicken.

Klar gibt es neuere Versionen, zB 182015 mit Vladimirs Patch für 
PR50775. Auf AVR wurde er nicht getestet und ich kab keine Zeit, mir 
anzuschauen, ob er für AVR was bringt.

> Das einzige, was ich an dem Code geändert habe, ist es, das Patch
> anzuwenden:
>
> http://www.mail-archive.com/gcc@gcc.gnu.org/msg61130.html
>
> Durch die Einführung dieser Address Spaces wurde vor etwa drei oder vier
> Wochen die Erzeugung des LTO-Frontends korrumpiert. LTO funktioniert mit
> dem Patch wieder.

Ja, ich weiß. Den Patch kann ich nicht einspielen weil bei mir LTO nicht 
funktioniert (collect2 spinnt und ruft denn falschen Linker auf) so daß 
ich keine Testsuite laufen lassen kann, ohne daß alle LTO-Tetss in die 
Hose gehen.

Weil ich das Problem wohl selbst beheben muss, ich mich in dem Teil von 
GCC aber nicht auskenne — GCC ist kein Ponyhof — hab ich schon 
mindestens 2 Wochen verloren ohne auch nur 1 Schritt vorwärts zu 
kommen...

> Übrigens funktionierte LTO seit der ersten Version
> (also GCC 4.5.0) an zuverlässig und korrekt, auch wenn im Internet
> Gegenteiliges zu lesen ist!

Ich hab nur Ärger mit LTO, s.o. Und anderes geht auch nicht, zB Canadian 
Cross Build für einen LTO-fähigen avr-gcc.

von (prx) A. K. (prx)


Lesenswert?

Lars R. schrieb:

> Was hat bei meinen Beispielen gefehlt? "m_CurrentDelta" und
> "m_LastState" haben gefehlt. Stimmt. Sorry:

Im Idealfall kommt Testcode vollständig ohne #include aus und enthält 
ausschliesslich die Statements, um die es bei dem Problem geht. Wenn man 
in die GCC Debug-Dumps reinsehen will, dann ersäuft man ungern im Output 
von tausend Zeilen überflüssiger Deklarationen.

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Das Problem ist, daß, um überhaupt zu den Skip-Befehlen zu kommen,
> Befehle vereinbart werden müssen, die ein "conditional jump on bit"
> machen. Weil das Sprungziel irgendwo liegen kann, wird dieser
> (Pseudo)Befehl dann ausgegeben als Skip-über-RJMP.
>
> Nur wenn genau eine Instruktion zu überspringen ist kann das RJMP
> wegfallen. Instruktionslängen sind erst sehr spät im Compiliervorgang
> bekannt jedoch nicht zu der Zeit, zu der "conditional jump on bit"
> erzeugt wird.

Dass die Architektur des GCC an sich eher "suboptimal" ist, habe ich 
mittlerweile selbst auch schon erkannt. LTO usw. sind für mich 
Erweiterungen des Grundprogramms, welche versuchen, diese Mängel zu 
beheben.

Das Grundproblem dürfte aber genau darauf zurück zu führen sein, dass 
der GCC selbst gar nicht "weiß", welchen Code er gerade eigentlich 
erzeugt. Es wird relativ früh bereits Assembler ausgegeben und dadurch 
gehen viele Ansatzpunkte der möglichen Optimierung verloren. Und um dies 
zu kompensieren, müssen viele Vorkehrungen relativ früh getroffen werden 
um so in die Codeerzeugung doch noch regulierend eingreifen zu können.

Andere Compiler nutzen hier mehrere verschiedene interne 
Darstellungsformen, welche von "ziemlich abstrakt" bis 
"maschinenabhängig" reichen. Dadurch ergeben sich natürlich viele 
Möglichkeiten.

Aber GCC ist ja vorrangig für die großen Maschinen gedacht. Da gibt es 
Speicher eigentlich immer in Hülle und Fülle, so dass Korrekturen 
betreffend zwei bis vier Bytes Codegröße vermutlich gar nicht erst Ernst 
genommen werden.

Umso erstaunlicher finde ich es aber, dass der avr-gcc - meines 
Erachtens - dennoch den kompaktesten Code erzeugt. Und ich habe gewiss 
schon einige Compiler für den AVR ausprobiert.

Johann L. schrieb:
> Mit Änderungen/Erweiterungen/Optimierungen im AVR-Teil dürfte das
> absolut garnix zu tun haben, weil es dort von 4.3 bis 4.6
> einschliesslich de facto überhaupt keine Ändrungen gab.

Die Teile von GCC, die den AVR betreffen, sind winzig. Ich weiß nicht 
"wo" überall Verbesserungen eingebracht wurden, aber es fällt auf, dass 
bis etwa zur Version 4.3 der Code immer größer wurde und ab etwa Version 
4.5 der Code wieder kleiner wird. Vielleicht erreicht man ja irgendwann 
wieder die Werte von Version 3...

Johann L. schrieb:
> Solche Artefakte zu debuggen und die Ursache zu finden, ist mir ehrlich
> gesagt zu aufwändig. Allein die Stelle zu finden dürfte mehrere Wochen
> dauerm. Das Problem sachgemäß zu beheben dauert idR nochmal so lange,
> und für eine Instruktion ist mir der Aufwand einfach zu hoch. Zudem
> geschieht es in dem Teil von GCC, der für alle Architekturen verwendet
> wird, also auch für 64-Bit Systeme etc. Und dort Regression-Tests und
> Benchmaks zu machen, ist definitiv jenseits von dem, was ich beitragen
> kann.

Ich vermute hier eher ein grundlegenderes Problem, welches vermutlich 
auch andere Prozessorarchitekturen in irgendeiner Form tangieren könnte. 
Falls diese etwaigen Auswirkungen dort aber ebenfalls so marginal sein 
sollten, gibt man sich halt mit dem aktuellen Stand zufrieden. Kann ich 
verstehen, würde ich vermutlich auch nicht anders machen.

Ist ja okay, wenn du schreibst, dass du das nicht beitragen kannst. Ich 
werde jedenfalls die Sache weiter beobachten, mit fast jeder Version 
haben sich bisher Kleinigkeiten bei den Registern verbessert. Am 
deutlichsten waren diese Änderungen aber bisher zwischen den Versionen 
4.6 und 4.7. Wenn es dazwischen kleinere Rückschritte gibt, nehme ich 
das notfalls mit in Kauf. Insgesamt hat sich die Codequalität ja sowieso 
verbessert.

Johann L. schrieb:
> Das einzige, was vielleicht helfen könnte, ist einen aussagekräftigen
> Bugreport zu machen. Die Chance, daß er Beachtung findet, ist allerdings
> sehr klein, denn AVR ist eine "unwichtige" Plattform.
>
> AVR ist "unwichtig", weil es nicht genug Entwickler gibt, die sich um
> den AVR-Teil kümmern. Zudem hat der AVR-Teil andere Einschränkungen; so
> ist er zB nicht standardkonform weil double = 32Bit etc.

Ich habe noch nie Gleitkommazahlen beim AVR gebraucht. Ob diese nun 32 
Bit haben oder nicht ist mir völlig gleichgültig, solange meine 
Anwendungsgebiete von GCC dadurch nicht in Mitleidenschaft gezogen 
werden. Ich setze den avr-gcc nun seit etwa vier bis fünf Jahren ein, 
selbst kompilieren und testen mache ich seit etwa anderthalb Jahren.

So einen Bugreport würde ich vielleicht dann in Erwägung ziehen, wenn 
falscher Code oder wirklich aufgeblähter Code erzeugt würde. Wegen 
dieser Kleinigkeiten will ich niemand "offiziell" aufhalten.

Johann L. schrieb:
> Die Gründe hier darzulegen führt mir zu weit. Vielleicht ist dein
> Interesse ja so hoch, daß du einen Blick unter die Motorhaube wirfst? In
> dem Fall wäre es dann auch möglich, sich auf einer technischen Ebene zu
> unterhalten anstatt nicht-zutreffende Bildchen verwenden zu müssen.

Das, was ich vom GCC weiß, habe ich aus meinem eigenem Quellcodestudium. 
Eine umfassende Dokumentation habe ich noch nicht gefunden, die wird es 
vermutlich auch nicht geben. Mit der Zeit werde ich sicherlich auch 
irgendwann besser durchblicken als jetzt. Wie bist du dazu gekommen?

Johann L. schrieb:
> Ja, ich weiß. Den Patch kann ich nicht einspielen weil bei mir LTO nicht
> funktioniert (collect2 spinnt und ruft denn falschen Linker auf) so daß
> ich keine Testsuite laufen lassen kann, ohne daß alle LTO-Tetss in die
> Hose gehen.
>
> Weil ich das Problem wohl selbst beheben muss, ich mich in dem Teil von
> GCC aber nicht auskenne — GCC ist kein Ponyhof — hab ich schon
> mindestens 2 Wochen verloren ohne auch nur 1 Schritt vorwärts zu
> kommen...

Du kannst aber doch den Patch einfügen und einfach LTO nicht aktivieren. 
Standardmäßig ist das doch beim AVR ausgeschaltet, oder? Ich muss 
jedenfalls drei Optionen angeben um LTO beim AVR zu aktivieren:

--enable-languages=lto
--enable-lto
--with-libelf-include=...

Und damit es bei mir keine Probleme gibt braucht es seit GCC 4.7 noch:

--disable-libquadmath

Johann L. schrieb:
> Ich hab nur Ärger mit LTO, s.o. Und anderes geht auch nicht, zB Canadian
> Cross Build für einen LTO-fähigen avr-gcc.

Welches "Canadian Cross Build" meinst du? Unter Linux den avr-gcc für 
Windows erzeugen?

Das, was du beschreibst, betrifft vor Allem dich als Entwickler von GCC. 
Für mich als Anwender mit realem Code gibt es keine Probleme. Ich nutze 
momentan bereits seit längerer Zeit den avr-gcc 4.5.3 mit aktivem LTO 
und habe noch kein Problem erlebt, welches darauf zurück zu führen wäre.

Ich wäre schon zufrieden, wenn ich aus den offiziellen Quellen wieder, 
auf meine eigene Gefahr hin, den LTO-avr-gcc erzeugen könnte, wie 
bereits seit Version 4.5.0.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Das Problem ist, daß, um überhaupt zu den Skip-Befehlen zu kommen,
>> Befehle vereinbart werden müssen, die ein "conditional jump on bit"
>> machen. Weil das Sprungziel irgendwo liegen kann, wird dieser
>> (Pseudo)Befehl dann ausgegeben als Skip-über-RJMP.
>>
>> Nur wenn genau eine Instruktion zu überspringen ist kann das RJMP
>> wegfallen. Instruktionslängen sind erst sehr spät im Compiliervorgang
>> bekannt jedoch nicht zu der Zeit, zu der "conditional jump on bit"
>> erzeugt wird.
>
> Dass die Architektur des GCC an sich eher "suboptimal" ist, habe ich
> mittlerweile selbst auch schon erkannt. LTO usw. sind für mich
> Erweiterungen des Grundprogramms, welche versuchen, diese Mängel zu
> beheben.

LTO ist eher der Versuch, gegen Schwächen anzukämpfen, die aus dem 
klassischen C-Workflow resultieren: Alle C-Dateien werden einzeln zu 
Objekten übersetzt und dieser Objekt-Zoo dann zu einer Anwendung 
zusammengebunden. Beim Übersetzten einer C-Datei wird alles ausserhalb 
der Datei als Blackbox behandelt und lediglich durch Interfaces 
(Prototypen) beschrieben. Das funktioniert alles ganz fein solange man 
nicht Informationen aus den anderen C-Dateien zum Optimieren verwenden 
will, ohne diesen gewohnen Workflow zu ersetzen.

Tatsächlich ist LTO m.E. keine "Link-Time-Optimization", sondern 
lediglich ein "Rerun-Compiler-at-Linktime", d.h. is funktioniert so, als 
würde man dem Compiler alle C-Dateien gleichzeitig vorwerfen anstatt 
durch einzelne Aufrufe.

> Das Grundproblem dürfte aber genau darauf zurück zu führen sein, dass
> der GCC selbst gar nicht "weiß", welchen Code er gerade eigentlich
> erzeugt. Es wird relativ früh bereits Assembler ausgegeben und dadurch

Nö. Aus GCC-Sicht ist Assembler der allerletzte Schritt von grob 
geschätzt 200 Passes. Alle Passes verwenden Abstraktionen des 
Programms in einer von mindestens 4 Zwischendarstellung:

• Gimple (Generic + Simple)
• tree-SSA (static single assignment)
• non-strict RTL
• strict RTL

> gehen viele Ansatzpunkte der möglichen Optimierung verloren. Und um dies
> zu kompensieren, müssen viele Vorkehrungen relativ früh getroffen werden
> um so in die Codeerzeugung doch noch regulierend eingreifen zu können.
>
> Andere Compiler nutzen hier mehrere verschiedene interne
> Darstellungsformen, welche von "ziemlich abstrakt" bis
> "maschinenabhängig" reichen. Dadurch ergeben sich natürlich viele
> Möglichkeiten.

Ist beim GCC auch so, siehe oben.

In aller Regel sind die Optimierungs-Patzer darauf zurückzuführen, daß 
GCC ein Multi-Pass-Compiler ist. Beispiel:

In einer Schleife wird eine Invariante gefunden. Ist es günstig, die 
Invariante vor die Schleife zu ziehen, um sie nicht immer wieder neu zu 
Berechnen/Laden?

Wenn man die Invariante rauszieht, bewirkt das eine Erhöhung der 
Registerlast, die bei der Registerallokierung u.U. unangenehm aufstößt, 
weil einem die Register ausgehen und man Platz auf dem Stack braucht.

Man muss also eine Vorstellung von der Registerlast haben, ohne eine 
Registerallokierung wirklich ausführen zu können (beachte, daß bis zur 
Registerallokierung womöglich noch 100 andere Passes zum Laufen kommen, 
die am Code rumklimpern).

Die Registerallokierung früh zu veranstalten, ist keine Lösung, weil 
sich dadurch so viele Abhängigkeiten zwischen den wenigen Registern 
ergeben, daß praktisch keine Optimierung mehr möglich ist.

Naturgemäß ebenfalls nicht möglich ist, nach der Registeralokierung den 
Code in SSA zu haben; der Darstellung, auf der maschinenunabhängige 
Optimierer und Analyse-Tools (alias-Analyse, Data- und Code-Flow, ...) 
aufsetzen. Die Registerallokierung entspricht übrigens dem Übergang 
non-strict RTL -> strict RTL.

Zudem: Nehemn wir an, ein Compiler würde ebensogute Ergebnisse 
produzieren wie ein Mensch, dem im Genenzug aber auch die gleiche Zeit 
zugestanden wird, um über dem Problem zu brüten. Wer würde einen 
Compiler akzeptieren, der über jedem Modul 10 Minuten oder länger 
brütet? Selbst bei 30 Sekunden pro 10000 Zeilen bekämen die Leute ja 
schon nen Herzinfarkt

Um diese Schwäche zu überwinden, müsste eine komplett neue 
Compiler-Technologie her mitsamt spekulativer Compilierung und one-Pass 
Ansatz. Da die zugrundeliegende Aufgabe jedoch alles andere als trivial 
ist und praktisch alle zu lösenden Teilaufgaben (Registerallokierung, 
Scheduling, ...) np-vollständig sind, dürfte ein solcher Ansatz zu 
nicht-akteptablen Compilezeiten und nicht wartbarem Compiler führen. Und 
ob es eine theoretische Basis dafür gibt...?

> Aber GCC ist ja vorrangig für die großen Maschinen gedacht. Da gibt es
> Speicher eigentlich immer in Hülle und Fülle, so dass Korrekturen
> betreffend zwei bis vier Bytes Codegröße vermutlich gar nicht erst Ernst
> genommen werden.
>
> Umso erstaunlicher finde ich es aber, dass der avr-gcc - meines
> Erachtens - dennoch den kompaktesten Code erzeugt. Und ich habe gewiss
> schon einige Compiler für den AVR ausprobiert.

Selbst für die Großen ist GCC besser als die Konkurrenz. Der neueste 
LLVM mit -O2/-O3 entsprich im Ergebnis etwa GCC 4.1 mit -O1.

Was überflüssige MOVs angeht, so dürfte das auf einer Architektur wie 
x86, die ohnehin nicht registerbasiert ist, nicht auffallen.

> Johann L. schrieb:
>> Mit Änderungen/Erweiterungen/Optimierungen im AVR-Teil dürfte das
>> absolut garnix zu tun haben, weil es dort von 4.3 bis 4.6
>> einschliesslich de facto überhaupt keine Ändrungen gab.
>
> Die Teile von GCC, die den AVR betreffen, sind winzig. Ich weiß nicht
> "wo" überall Verbesserungen eingebracht wurden, aber es fällt auf, dass
> bis etwa zur Version 4.3 der Code immer größer wurde und ab etwa Version
> 4.5 der Code wieder kleiner wird. Vielleicht erreicht man ja irgendwann
> wieder die Werte von Version 3...

Bislang hab ich noch keine Version von 4.x gesehen — einschliesslich den 
angetesteten 4.7 Snapshots — die für meine Projekte an Codedichte und 
Geschwindigkeit an 3.4.6 rankommt. Teilweise sind die 4.x bis zu 20% 
schlechter, selbst nach Ausschöpfen aller Schalter-Tricks.

> Johann L. schrieb:
>> Solche Artefakte zu debuggen und die Ursache zu finden, ist mir ehrlich
>> gesagt zu aufwändig. Allein die Stelle zu finden dürfte mehrere Wochen
>> dauerm. Das Problem sachgemäß zu beheben dauert idR nochmal so lange,
>> und für eine Instruktion ist mir der Aufwand einfach zu hoch. Zudem
>> geschieht es in dem Teil von GCC, der für alle Architekturen verwendet
>> wird, also auch für 64-Bit Systeme etc. Und dort Regression-Tests und
>> Benchmaks zu machen, ist definitiv jenseits von dem, was ich beitragen
>> kann.
>
> Ich vermute hier eher ein grundlegenderes Problem, welches vermutlich
> auch andere Prozessorarchitekturen in irgendeiner Form tangieren könnte.

Nicht nur tangieren, du bist hier mitten im Getümmel des 
kompliziertesten und eines der sensibelsten Teile des Compilers!

> Falls diese etwaigen Auswirkungen dort aber ebenfalls so marginal sein
> sollten, gibt man sich halt mit dem aktuellen Stand zufrieden. Kann ich
> verstehen, würde ich vermutlich auch nicht anders machen.
>
> Ist ja okay, wenn du schreibst, dass du das nicht beitragen kannst. Ich
> werde jedenfalls die Sache weiter beobachten, mit fast jeder Version
> haben sich bisher Kleinigkeiten bei den Registern verbessert. Am
> deutlichsten waren diese Änderungen aber bisher zwischen den Versionen
> 4.6 und 4.7. Wenn es dazwischen kleinere Rückschritte gibt, nehme ich
> das notfalls mit in Kauf. Insgesamt hat sich die Codequalität ja sowieso
> verbessert.

Falls du mit indirekten Strukturzugriffen oder C++-Objekten arbeitest, 
lohnt sich evtl. die Option -mstrict-X, die nach Beheben von PR50775 
auch in etwa wie angedacht zu funktionieren scheint.

> Johann L. schrieb:
>> Das einzige, was vielleicht helfen könnte, ist einen aussagekräftigen
>> Bugreport zu machen. Die Chance, daß er Beachtung findet, ist allerdings
>> sehr klein, denn AVR ist eine "unwichtige" Plattform.
>>
>> AVR ist "unwichtig", weil es nicht genug Entwickler gibt, die sich um
>> den AVR-Teil kümmern. Zudem hat der AVR-Teil andere Einschränkungen; so
>> ist er zB nicht standardkonform weil double = 32Bit etc.
>
> Ich habe noch nie Gleitkommazahlen beim AVR gebraucht. Ob diese nun 32
> Bit haben oder nicht ist mir völlig gleichgültig, solange meine
> Anwendungsgebiete von GCC dadurch nicht in Mitleidenschaft gezogen
> werden.

Ich hab in Planung das zu ändern, aber wie gesagt: Zeit ist rar und es 
gibt genug andere Baustellen... Zudem macht die avr-libc mit 64-Bit 
double die Krätsche.

> Johann L. schrieb:
>> Die Gründe hier darzulegen führt mir zu weit. Vielleicht ist dein
>> Interesse ja so hoch, daß du einen Blick unter die Motorhaube wirfst? In
>> dem Fall wäre es dann auch möglich, sich auf einer technischen Ebene zu
>> unterhalten anstatt nicht-zutreffende Bildchen verwenden zu müssen.
>
> Das, was ich vom GCC weiß, habe ich aus meinem eigenem Quellcodestudium.
> Eine umfassende Dokumentation habe ich noch nicht gefunden, die wird es

Was am ehesten rankommt sind die Internals:
http://gcc.gnu.org/onlinedocs/gccint

Ansonsten hilft Lesen der Compilerquellen, insbesondere von Backends. 
Der BE-Code ist ja durch die Hooks relativ gut vom Rest des Compilers 
separiert, so daß man vom GCC-Gedärm nicht viel wissen muss. Man kann 
mit RTL und den Insns vertraus machen und auf dieser Zwischenebene sind 
die Dinge wesentlich besser zu erfassen als aus dem daraus durch die 
gen-Tools erzeugten C-Code.

> vermutlich auch nicht geben. Mit der Zeit werde ich sicherlich auch
> irgendwann besser durchblicken als jetzt. Wie bist du dazu gekommen?

Hobby-Knaudeln an AT89C2051, und dann hab ich irgendwann mehr oder 
weniger zufällt AVR + avr-gcc entdeckt. Für den AT89 schrieb ich noch in 
einem fürchterlichen, proprietären Assembler. Der Umstieg auf (avr)-gcc 
war dann wie ein Erlösung ;-)

Um besser zu verstehen, wie effeizienter C-Code aussehen sollte, hab ich 
mich näher mit GCC beschäftigt. Als es denn die 4.x-Versionen von WinAVR 
gab, war ich von deren Codegüte gründlich enttäuscht. Wie gesagt 
inakzeptabel schlechter als 3.4.6.

Ich also bin vom Basteln /mit/ avr-gcc zum Besteln /an/ avr-gcc 
gekommen, weil ich mir dachte: So schwer kann's eigentlich nicht sein, 
die 4.x dahin zu bringen, wo die 3.4 schon ist. Und wenn man das Faß 
ersma offen hat, sind da 1000 Dinge zu Flicken wie Bugs, kleinere 
Optimierungs-Patzer, Code-Cleanup, Dokumentation, neue Features wie 
24-Bit Typen, Named Addresses, Built-ins, etc.

> Johann L. schrieb:
>> Ja, ich weiß. Den Patch kann ich nicht einspielen weil bei mir LTO nicht
>> funktioniert (collect2 spinnt und ruft denn falschen Linker auf) so daß
>> ich keine Testsuite laufen lassen kann, ohne daß alle LTO-Tetss in die
>> Hose gehen.
>>
>> Weil ich das Problem wohl selbst beheben muss, ich mich in dem Teil von
>> GCC aber nicht auskenne — GCC ist kein Ponyhof — hab ich schon
>> mindestens 2 Wochen verloren ohne auch nur 1 Schritt vorwärts zu
>> kommen...
>
> Du kannst aber doch den Patch einfügen und einfach LTO nicht aktivieren.

Nein, kann ich nicht. Mit "einfügen" meine ich ins GCC SVN.

> Standardmäßig ist das doch beim AVR ausgeschaltet, oder? Ich muss
> jedenfalls drei Optionen angeben um LTO beim AVR zu aktivieren:
>
> --enable-languages=lto
> --enable-lto
> --with-libelf-include=...

Das einzige, was ich brauche, ist ein --enable-plugins bei den binutils 
(ich verwende 2.21), mehr nicht. Im GCC ist LTO inzwischen Standard.

> Und damit es bei mir keine Probleme gibt braucht es seit GCC 4.7 noch:
>
> --disable-libquadmath

Hab ich bei anderen schon gesehen; ich selbst verwende sie nicht. Was 
für Probleme gibt's da?

> Johann L. schrieb:
>> Ich hab nur Ärger mit LTO, s.o. Und anderes geht auch nicht, zB Canadian
>> Cross Build für einen LTO-fähigen avr-gcc.
>
> Welches "Canadian Cross Build" meinst du? Unter Linux den avr-gcc für
> Windows erzeugen?

Jepp. Das Problem ist dlopen-Support in der Cross-Build Umgebung.

> Das, was du beschreibst, betrifft vor Allem dich als Entwickler von GCC.
> Für mich als Anwender mit realem Code gibt es keine Probleme.

Ja, ich plage mich auf Entwicklerebene damit rum; wirklich eingesetzt 
für's Target hab ich LTO noch nie.

> Ich wäre schon zufrieden, wenn ich aus den offiziellen Quellen wieder,
> auf meine eigene Gefahr hin, den LTO-avr-gcc erzeugen könnte, wie
> bereits seit Version 4.5.0.

Das Patch für PR51409 ist inzwischen upstream, sollte also wieder alles 
out-of-the-box funktionieren.

von Simon K. (simon) Benutzerseite


Lesenswert?

Wenn Atmel doch endlich mal mehr Manpower einsetzen würde um am AVR-GCC 
herumzudoktorn!

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> LTO ist eher der Versuch, gegen Schwächen anzukämpfen, die aus dem
> klassischen C-Workflow resultieren: Alle C-Dateien werden einzeln zu
> Objekten übersetzt und dieser Objekt-Zoo dann zu einer Anwendung
> zusammengebunden. Beim Übersetzten einer C-Datei wird alles ausserhalb
> der Datei als Blackbox behandelt und lediglich durch Interfaces
> (Prototypen) beschrieben. Das funktioniert alles ganz fein solange man
> nicht Informationen aus den anderen C-Dateien zum Optimieren verwenden
> will, ohne diesen gewohnen Workflow zu ersetzen.

Richtig, im Grunde genommen hast du damit natürlich Recht. Die ersten 
C-Compiler haben so gearbeitet - leider heute auch immer noch GCC. Es 
"verbietet" aber eigentlich niemand, dass der Compiler auch etwas 
intelligenter sein darf. Gerade im Embedded-Bereich lässt man Compilern 
gerne die ein oder andere Abweichung von der offiziellen 
Sprachspezifikation durchgehen. Dazu schreibe ich weiter unten noch 
etwas.

Johann L. schrieb:
> Tatsächlich ist LTO m.E. keine "Link-Time-Optimization", sondern
> lediglich ein "Rerun-Compiler-at-Linktime", d.h. is funktioniert so, als
> würde man dem Compiler alle C-Dateien gleichzeitig vorwerfen anstatt
> durch einzelne Aufrufe.

Ganz so schwarz, wie du es hier skizzierst, ist LTO dann auch wieder 
nicht. Der Compiler kompiliert nicht alles noch einmal komplett neu. 
Eine der ersten internen Darstellungen des Moduls sichert GCC in den 
Objektdateien. Bei der eigentlichen LTO-Kompilierung vor dem Linken muss 
daher das C-Frontend nicht mehr aktiv werden, stattdessen werden die 
zuvor beim ersten Durchgang gesicherten Daten "recycelt". Dennoch dauert 
der ganze Vorgang mit LTO dadurch mehr als doppelt so lange. Stört 
zumindest meine Wenigkeit nicht wirklich, denn ich bin ja auch nicht auf 
der Flucht. Bisher habe ich immer länger gebraucht den Code erst einmal 
zu schreiben.

Johann L. schrieb:
> Nö. Aus GCC-Sicht ist Assembler der allerletzte Schritt von grob
> geschätzt 200 Passes. Alle Passes verwenden Abstraktionen des
> Programms in einer von mindestens 4 Zwischendarstellung:

Natürlich hast du auch hier wieder Recht. Aber du hast mich eventuell 
etwas missverstanden. In dem Schritt, in dem der Assembler generiert 
wird, passiert vieles "von oben herab" - um mal so zu formulieren. 
Deshalb meinte ich, dass der Code zu schnell ausgegeben wird. Also 
bezogen genau auf diesen letzten Schritt. Der Compiler ist viel zu 
kleinkariert, selbst innerhalb einer C-Datei betrachtet er jede Funktion 
mehr oder weniger unabhängig.

Johann L. schrieb:
> Zudem: Nehemn wir an, ein Compiler würde ebensogute Ergebnisse
> produzieren wie ein Mensch, dem im Genenzug aber auch die gleiche Zeit
> zugestanden wird, um über dem Problem zu brüten. Wer würde einen
> Compiler akzeptieren, der über jedem Modul 10 Minuten oder länger
> brütet? Selbst bei 30 Sekunden pro 10000 Zeilen bekämen die Leute ja
> schon nen Herzinfarkt

Hast du schon einmal eine MFC-Anwendung mit Microsoft Visual C++ 
kompiliert? Glaube mir, zehn Minuten kommen da schnell zusammen. Der 
avr-gcc dürfte ruhig für 32 Kilobytes Code zwei, drei Minuten 
"Bedenkzeit" in Anspruch nehmen. Hätte ich kein Problem mit. ;-)

Johann L. schrieb:
> Um diese Schwäche zu überwinden, müsste eine komplett neue
> Compiler-Technologie her mitsamt spekulativer Compilierung und one-Pass
> Ansatz. Da die zugrundeliegende Aufgabe jedoch alles andere als trivial
> ist und praktisch alle zu lösenden Teilaufgaben (Registerallokierung,
> Scheduling, ...) np-vollständig sind, dürfte ein solcher Ansatz zu
> nicht-akteptablen Compilezeiten und nicht wartbarem Compiler führen. Und
> ob es eine theoretische Basis dafür gibt...?

Das wird wohl genau das Problem sein. Theorie. Ich brauche Code für die 
Praxis. Es gibt - für andere Prozessorarchitekturen - sehr gute 
Compiler! Siehe weiter unten.

Johann L. schrieb:
> Selbst für die Großen ist GCC besser als die Konkurrenz. Der neueste
> LLVM mit -O2/-O3 entsprich im Ergebnis etwa GCC 4.1 mit -O1.

Also stimmst du mir zu, dass der erzeugte Code besser geworden ist...

Johann L. schrieb:
> Bislang hab ich noch keine Version von 4.x gesehen — einschliesslich den
> angetesteten 4.7 Snapshots — die für meine Projekte an Codedichte und
> Geschwindigkeit an 3.4.6 rankommt. Teilweise sind die 4.x bis zu 20%
> schlechter, selbst nach Ausschöpfen aller Schalter-Tricks.

... weil hier widersprichst du dir etwas. Ich habe nicht geschrieben, 
dass der Code mit 4.7 schon wieder so gut wie der von 3.x ist. Aber er 
ist mit 4.7 einen großen Schritt in diese Richtung gekommen!

avr-gcc Version 3.4.6 war eine große Katastrophe. Der Code war zwar 
besser, aber wirklich stabil war das Teil auch nicht. Ich würde nicht 
mehr tauschen wollen, anfangs hielt ich mich auch etwas zurück, aber 
seit 4.5.x überwiegen für mich eindeutig die Vorteile.

Johann L. schrieb:
> Falls du mit indirekten Strukturzugriffen oder C++-Objekten arbeitest,
> lohnt sich evtl. die Option -mstrict-X, die nach Beheben von PR50775
> auch in etwa wie angedacht zu funktionieren scheint.

Das werde ich später mal versuchen.

Johann L. schrieb:
> Was am ehesten rankommt sind die Internals:
> http://gcc.gnu.org/onlinedocs/gccint
>
> Ansonsten hilft Lesen der Compilerquellen, insbesondere von Backends.
> Der BE-Code ist ja durch die Hooks relativ gut vom Rest des Compilers
> separiert, so daß man vom GCC-Gedärm nicht viel wissen muss. Man kann
> mit RTL und den Insns vertraus machen und auf dieser Zwischenebene sind
> die Dinge wesentlich besser zu erfassen als aus dem daraus durch die
> gen-Tools erzeugten C-Code.

In den Code lese ich mich ja schon ein. Dauert halt bloß. Meine Freizeit 
ist leider auch begrenzt. Ich werde die anderen Infos auch entsprechend 
versuchen zu verstehen.

Johann L. schrieb:
> Hobby-Knaudeln an AT89C2051, und dann hab ich irgendwann mehr oder
> weniger zufällt AVR + avr-gcc entdeckt. Für den AT89 schrieb ich noch in
> einem fürchterlichen, proprietären Assembler. Der Umstieg auf (avr)-gcc
> war dann wie ein Erlösung ;-)

Mit den 8051 habe ich fast täglich zu tun. Das meiste erledige ich dabei 
aber auch mit C. Ich nutze den C51 dafür.

Es gibt einen Freeware-Assembler für die 8051:

http://plit.de/asem-51/

Kommen wir auch schon zum Thema: Der C51 erzeugt wunderbaren Code aus 
den C-Dateien. Das Teil ist glaube ich schon steinalt und funktioniert 
einwandfrei. Der Compiler ist, alleine schon von der Dateigröße her zu 
urteilen, niemals so komplex wie es der GCC ist. Der Code ist dabei so 
dicht, dass man meistens noch nicht einmal mehr eine Möglichkeit fände 
um nur ein einzelnes Byte einsparen zu können!

Die vorhandenen Optimierungsstufen sind auch weit weniger ausführlich 
und kompliziert wie die des GCC. Hauptsächlich findet die Arbeit durch 
einen simplen Peephole-Optimizer statt. Du magst nun zwar vielleicht 
schon schmunzeln, aber unterschätze nicht, wie viel Zeit die schon 
investiert haben!

Der "Trick", den dieser Compiler nutzt, ist es, zu wissen, was er 
produziert hat! Und dieses Wissens wird für die weitere Codeerzeugung 
ausgenutzt. Es gibt beispielsweise die Option "Global Register 
Coloring". Dabei wählt der Compiler die Register so aus, dass möglichst 
wenig Kollisionen zu den vorherigen Registerbelegungen entstehen. Die 
Registerbelegungen werden in weiteren Durchläufen soweit verbessert, bis 
sie okay sind. Der Compiler muss dazu halt manchmal sechs, sieben Male 
den Code nachbearbeiten bis es passt. Aber es funktioniert.

Der avr-gcc betrachtet jede Funktion für sich, die Parameter werden 
immer angefangen mit r24 übergeben usw. Wenn ein temporäres Register 
genutzt wird, wird push/pop erzeugt ohne dass dies eventuell nötig wäre. 
Daran "krankt" das Teil.

Angenommen ich rufe in der Funktion "a" in der Datei a.c die Funktion 
"b" aus der Datei b.c auf. Nun braucht "a" ein temporäres Register "r12" 
zum Beispiel, also kommt zu Beginn push und zum Ende pop. Aber 
vielleicht braucht ja "b" gar nicht "r12". Dann hätte man sich das 
sparen können.

Der einzige Nachteil durch dieses Verfahren ist, dass man Assemblercode 
und C nicht nach belieben mischen kann, da es keine immer gültige 
Registerbelegung gibt.

Bedenke jetzt, dass ein 8051 nur acht Register und einen Pointer hat. 
Der AVR hat zehn Register, drei Pointer und sechzehn "mehr oder weniger" 
gut zu gebrauchende Register. Es sollte also hier noch leichter sein, so 
etwas zu implementieren.

Sicher, der GCC ist dafür nicht ausgelegt. Aber dieses Verfahren ist 
auch ohne große Theorie implementierbar. Und die Kunden sind zufrieden. 
Jedenfalls ist es der Industriestandard für 8051-C-Compiler!

Johann L. schrieb:
> Ich also bin vom Basteln /mit/ avr-gcc zum Besteln /an/ avr-gcc
> gekommen, weil ich mir dachte: So schwer kann's eigentlich nicht sein,
> die 4.x dahin zu bringen, wo die 3.4 schon ist. Und wenn man das Faß
> ersma offen hat, sind da 1000 Dinge zu Flicken wie Bugs, kleinere
> Optimierungs-Patzer, Code-Cleanup, Dokumentation, neue Features wie
> 24-Bit Typen, Named Addresses, Built-ins, etc.

24-Bit-Ganzzahlen wären schon etwas feines. Wenn du das umsetzen 
könntest, wäre es eine Bereicherung. Mit den 32-Bit-Zahlen tut er sich 
ja auch etwas schwer.

Johann L. schrieb:
> Das einzige, was ich brauche, ist ein --enable-plugins bei den binutils
> (ich verwende 2.21), mehr nicht. Im GCC ist LTO inzwischen Standard.

Ich verwende auch 2.21, aber ohne --enable-plugins. Vielleicht liegt es 
daran? Den GCC kannst du aber immer noch mit --disable-lto daran hindern 
dies auch umzusetzen.

Johann L. schrieb:
> Hab ich bei anderen schon gesehen; ich selbst verwende sie nicht. Was
> für Probleme gibt's da?

Die Kompilierung dauert dreimal solange, wenn man dies nicht abschaltet. 
Weiterhin verabschiedet sich der GCC dann auch gerne ab und zu mit ICE. 
Da ich diese Funktionalität nicht brauche, habe nicht lange geforscht 
sondern einfach abgeschaltet. ;-)

Johann L. schrieb:
> Jepp. Das Problem ist dlopen-Support in der Cross-Build Umgebung.

Ich erzeuge mir den Windows-avr-gcc direkt mit MinGW. Dauert zwar auch 
ewig, ich habe dann aber die Gewissheit das es auch funktioniert.

Ich nutze LTO so gerne, weil dies gerade die oben beschriebenen 
Nachteile ausgleicht. Viele push/pop-Szenarien entfallen dadurch, die 
sich gegenseitig aufrufenden Funktionen werden ineinander gezogen, 
gleiche Zwischenergebnisse werden wiederverwendet und um Inlining 
braucht man sich gar keine Gedanken mehr zu machen, weil der Compiler 
dies dann automatisch macht wenn es sinnvoll ist - und das sogar über 
Modulgrenzen hinweg!

Gerade durch dieses Feature kam ich dazu avr-gcc 4.5 selbst zu testen, 
ich las, dass dieses Feature in Entwicklung ist und dann reizte es mich 
zu sehen, ob dies auch mit den AVR funktionieren würde und dies tat es - 
sogar großartig.

von (prx) A. K. (prx)


Lesenswert?

Lars R. schrieb:

> avr-gcc dürfte ruhig für 32 Kilobytes Code zwei, drei Minuten
> "Bedenkzeit" in Anspruch nehmen. Hätte ich kein Problem mit. ;-)

Ob dir bei dieser Ansicht viele beipflichten werden? Zweifellos werden 
die Anwender der 256KB Versionen darüber hoch erfreut sein, wenn ihr 
Programm nach gut 10 Minuten nun 140KB statt 142KB gross ist.

Und die Aussicht, dass die Freitags gestartete Kompilierung vom 
Linux-Kernel bereits nach dem Wochenende fertig sein wird, wird endlich 
für den längst erforderlichen Innovationsschub in der PC-Branche sorgen, 
denn bisher weiss ja niemand so richtig, wozu man 128 Prozessoren in 
non-Gamer PCs brauchen kann. ;-)

von Lars R. (larsr)


Lesenswert?

A. K. schrieb:
> Ob dir bei dieser Ansicht viele beipflichten werden? Zweifellos werden
> die Anwender der 256KB Versionen darüber hoch erfreut sein, wenn ihr
> Programm nach gut 10 Minuten nun 140KB statt 142KB gross ist.
>
> Und die Aussicht, dass die Freitags gestartete Kompilierung vom
> Linux-Kernel bereits nach dem Wochenende fertig sein wird, wird endlich
> für den längst erforderlichen Innovationsschub in der PC-Branche sorgen,
> denn bisher weiss ja niemand so richtig, wozu man 128 Prozessoren in
> non-Gamer PCs brauchen kann. ;-)

Scherzkeks! Optimierungsstufen auswählen sollte man schon noch können...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> LTO ist eher der Versuch, gegen Schwächen anzukämpfen, die aus dem
>> klassischen C-Workflow resultieren: Alle C-Dateien werden einzeln zu
>> Objekten übersetzt und dieser Objekt-Zoo dann zu einer Anwendung
>> zusammengebunden. Beim Übersetzten einer C-Datei wird alles ausserhalb
>> der Datei als Blackbox behandelt und lediglich durch Interfaces
>> (Prototypen) beschrieben. Das funktioniert alles ganz fein solange man
>> nicht Informationen aus den anderen C-Dateien zum Optimieren verwenden
>> will, ohne diesen gewohnen Workflow zu ersetzen.
>
> Richtig, im Grunde genommen hast du damit natürlich Recht. Die ersten
> C-Compiler haben so gearbeitet - leider heute auch immer noch GCC.

Nein, GCC arbeitet nicht so, sondern die Makefiles der Anwender.
Alles, was diese tun müssten, ist, alle Quellen in einem Aufruf an GCC 
zu übergeben. Bleibt die Frage, wie dann der Compile-Aufruf für FireFox 
oder GCC selbst aussähe...

> Es "verbietet" aber eigentlich niemand, dass der Compiler auch etwas
> intelligenter sein darf. Gerade im Embedded-Bereich lässt man Compilern
> gerne die ein oder andere Abweichung von der offiziellen
> Sprachspezifikation durchgehen. Dazu schreibe ich weiter unten noch
> etwas.

Wie die Leute ihre Makefiles schreiben, hat ja nix direkt mit 
Sprachstandards zu tun, sondern sie wollen einfach Zeit sparem beim 
(mehrfachen) Compilieren.

> Johann L. schrieb:
>> Tatsächlich ist LTO m.E. keine "Link-Time-Optimization", sondern
>> lediglich ein "Rerun-Compiler-at-Linktime", d.h. is funktioniert so, als
>> würde man dem Compiler alle C-Dateien gleichzeitig vorwerfen anstatt
>> durch einzelne Aufrufe.
>
> Ganz so schwarz, wie du es hier skizzierst, ist LTO dann auch wieder
> nicht. Der Compiler kompiliert nicht alles noch einmal komplett neu.

Naja, aber mindesten 95%. Was gespart wird, ist Lexen und Parsen, viel 
mehr nicht. Alles, was danach kommt und schon einmal mühsam in mehreren 
100 Passes zusammengetragen wurde, ist Makulatur und muss nochmals 
gemacht werden, zuzüglich File I/O und (De)Serialisierung.

Daß LTO keine Optimierung zur Linkzeit ist, sieht man auch daran, daß 
Informationen, die erst dem Linker bekannt sind, nicht zur Optimierung 
per LTO herangezogen werden können.

Nimm folgenden Einzeiler auf einer Architektur, die nicht auf den 
gesamten Speicher direkt zugreifen kann:
1
int add (void) { extern int a, b; return a + b; }
Übersetzt wird das dann etwa so:
1
R1 = &a
2
R2 = R1[0]
3
R1 = &b
4
R1 = R1[0]
5
R2 += R1
6
return R1
Liegen a und b jedoch nah beieinander — was erst dem Linker/Locator 
bekannt ist — und hätte man die Information in Compiler, ginge:
1
R1 = &a
2
R2 = R1[0]
3
R1 = R1[&b-&a]
4
R2 += R1
5
return R1

> Eine der ersten internen Darstellungen des Moduls sichert GCC in den
> Objektdateien.

...wobei ich mich auch frage, was die proprietäre Welt davon hält. Die 
setz ja nicht ungern GCC sein...

> Johann L. schrieb:
>> Nö. Aus GCC-Sicht ist Assembler der allerletzte Schritt von grob
>> geschätzt 200 Passes. Alle Passes verwenden Abstraktionen des
>> Programms in einer von mindestens 4 Zwischendarstellung:
>
> Natürlich hast du auch hier wieder Recht. Aber du hast mich eventuell
> etwas missverstanden. In dem Schritt, in dem der Assembler generiert
> wird, passiert vieles "von oben herab" - um mal so zu formulieren.

Der Schritt der Assembler ausgibt, hat die Aufgabe, Assembler auszugeben 
:o) Wenn (Optimierungs)aufgaben bis dahin nicht gelöst wurden — etwa 
durch Target-spezifische Passes — hat das Target eben Pech und sollte an 
die Implementierung eines solchen Passes denken anstatt Stellen 
aufzubohren, die nicht dafür gedacht sind.

Das einzige, was in final noch gemacht wird, sind Text-Peepholes 
(deprecated) und cc0-Behandlung (deprecated). Wenn mich nicht alles 
täuscht, ist avr das einzige Target, das noch cc0 verwenden um 
ConditionCode abzubilden. Der Umbau von cc0 zu CCmode wäre allerdings 
gewaltig und brächte zunächst nichts ausser ein Sack voll neuer Bugs.

> Johann L. schrieb:
>> Zudem: Nehemn wir an, ein Compiler würde ebensogute Ergebnisse
>> produzieren wie ein Mensch, dem im Genenzug aber auch die gleiche Zeit
>> zugestanden wird, um über dem Problem zu brüten. Wer würde einen
>> Compiler akzeptieren, der über jedem Modul 10 Minuten oder länger
>> brütet? Selbst bei 30 Sekunden pro 10000 Zeilen bekämen die Leute ja
>> schon nen Herzinfarkt
>
> Hast du schon einmal eine MFC-Anwendung mit Microsoft Visual C++
> kompiliert? Glaube mir, zehn Minuten kommen da schnell zusammen.

Nun, ich schätze das sind *etwas* mehr als 1E5 Zeilen, die da zu 
Compileren sind?

> Johann L. schrieb:
>> Um diese Schwäche zu überwinden, müsste eine komplett neue
>> Compiler-Technologie her mitsamt spekulativer Compilierung und one-Pass
>> Ansatz. Da die zugrundeliegende Aufgabe jedoch alles andere als trivial
>> ist und praktisch alle zu lösenden Teilaufgaben (Registerallokierung,
>> Scheduling, ...) np-vollständig sind, dürfte ein solcher Ansatz zu
>> nicht-akteptablen Compilezeiten und nicht wartbarem Compiler führen. Und
>> ob es eine theoretische Basis dafür gibt...?
>
> Das wird wohl genau das Problem sein. Theorie. Ich brauche Code für die
> Praxis. Es gibt - für andere Prozessorarchitekturen - sehr gute
> Compiler! Siehe weiter unten.

Ohne Theorie geht eben nix. Beispiel von unten: Graph-Colorng für 
Registerallokierung.

> Johann L. schrieb:
>> Selbst für die Großen ist GCC besser als die Konkurrenz. Der neueste
>> LLVM mit -O2/-O3 entsprich im Ergebnis etwa GCC 4.1 mit -O1.
>
> Also stimmst du mir zu, dass der erzeugte Code besser geworden ist...

Meine Aussage bezieht sich auf Benchmarks wie SPEC2000 auf 64-Bit 
Boliden; für avr-gcc hab ich keine repräsentativen Vergleichsdaten.

> Johann L. schrieb:
>> Bislang hab ich noch keine Version von 4.x gesehen — einschliesslich den
>> angetesteten 4.7 Snapshots — die für meine Projekte an Codedichte und
>> Geschwindigkeit an 3.4.6 rankommt. Teilweise sind die 4.x bis zu 20%
>> schlechter, selbst nach Ausschöpfen aller Schalter-Tricks.
>
> ... weil hier widersprichst du dir etwas.

Diese Aussabe bezog sich jetzt auf avr-gcc. Sorry für die Verwirung.

> avr-gcc Version 3.4.6 war eine große Katastrophe. Der Code war zwar
> besser, aber wirklich stabil war das Teil auch nicht. Ich würde nicht
> mehr tauschen wollen, anfangs hielt ich mich auch etwas zurück, aber
> seit 4.5.x überwiegen für mich eindeutig die Vorteile.

Ich erlebe 3.4.6 als die stabilste Version überhaupt. Allerdings 
verwende ich die nur als WinAVR, also mit Patches. Ne naked 3.4.6 hatte 
ich nie im Einsatz.

> Johann L. schrieb:
>> Hobby-Knaudeln an AT89C2051, und dann hab ich irgendwann mehr oder
>> weniger zufällt AVR + avr-gcc entdeckt. Für den AT89 schrieb ich noch in
>> einem fürchterlichen, proprietären Assembler. Der Umstieg auf (avr)-gcc
>> war dann wie ein Erlösung ;-)
>
> Mit den 8051 habe ich fast täglich zu tun. Das meiste erledige ich dabei
> aber auch mit C. Ich nutze den C51 dafür.

8051 hab ich zum Glück hinter mir gelassen :-)

> Der avr-gcc betrachtet jede Funktion für sich, die Parameter werden
> immer angefangen mit r24 übergeben usw.

nennt sich ABI ;-)

> Wenn ein temporäres Register genutzt wird, wird push/pop erzeugt
> ohne dass dies eventuell nötig wäre. Daran "krankt" das Teil.

Doch, die ABI verlangt es.

> Angenommen ich rufe in der Funktion "a" in der Datei a.c die Funktion
> "b" aus der Datei b.c auf. Nun braucht "a" ein temporäres Register "r12"
> zum Beispiel, also kommt zu Beginn push und zum Ende pop. Aber
> vielleicht braucht ja "b" gar nicht "r12". Dann hätte man sich das
> sparen können.

Ja, nachher ist man immer schlauer. Nehmen wir an, wir machen eine 
entsprechende Optimierung und reichern die Assemebler-Ausgabe mit 
Anmerkungen an, welche Register wirklich verwendet werden.

Wie stellst du nun sicher, daß b aus Modul b verwendet wird und nicht b 
aus einem anderen Modul c? C erlaubt es: a ist nur das Interface bekannt 
und das verwendere ABI.

> Bedenke jetzt, dass ein 8051 nur acht Register und einen Pointer hat.

Klar, für einen GCC ist das zu klein.

> Der AVR hat zehn Register, drei Pointer und sechzehn "mehr oder weniger"
> gut zu gebrauchende Register. Es sollte also hier noch leichter sein, so
> etwas zu implementieren.

Wenn es leicht ist und der Ansatz der allgemein genug, sollte es doch 
kein Problem für die C51-Jungs und -Mädels sein, im Revier der 
AVR-Compiler zu wildern?

> Johann L. schrieb:
>> Ich also bin vom Basteln /mit/ avr-gcc zum Basteln /an/ avr-gcc
>> gekommen, weil ich mir dachte: So schwer kann's eigentlich nicht sein,
>> die 4.x dahin zu bringen, wo die 3.4 schon ist. Und wenn man das Faß
>> ersma offen hat, sind da 1000 Dinge zu Flicken wie Bugs, kleinere
>> Optimierungs-Patzer, Code-Cleanup, Dokumentation, neue Features wie
>> 24-Bit Typen, Named Addresses, Built-ins, etc.
>
> 24-Bit-Ganzzahlen wären schon etwas feines.
> Wenn du das umsetzen könntest, wäre es eine Bereicherung.

Sond doch schon drinne und hören auf __int24 und __uint24. Das einzige, 
was noch fehlt ist Multiplikation (__mulpsi3) und die *räusper* 
Dokumentation.

> Mit den 32-Bit-Zahlen tut er sich ja auch etwas schwer.

Die 3-Byte Typen sind auch clumsy, haben sich aber zusammen mit __pgmx 
angeboten.

> Johann L. schrieb:
>> Hab ich bei anderen schon gesehen; ich selbst verwende sie nicht. Was
>> für Probleme gibt's da?
>
> Die Kompilierung dauert dreimal solange, wenn man dies nicht abschaltet.

hä? libquadmath ist doch nur ne Lib wie libgcc auch? Die sind doch fix 
durch...

> Weiterhin verabschiedet sich der GCC dann auch gerne ab und zu mit ICE.
> Da ich diese Funktionalität nicht brauche, habe nicht lange geforscht
> sondern einfach abgeschaltet. ;-)

Nicht abschalten, sondern nen gscheiten Bug-Report machen!
War wahrscheinlich ein guter, alter Spill Failure?

Nochwas zu (Optimierungs)algorithmen in GCC: Es sind keine Algorithmen 
erlaubt, deren Laufzeit quadratisch oder gar exponentiell geht.

Damit fallen naive Ansätze für NP-vollständige Probleme wie 
Registerallokierung schon mal weg, und — Theorie und Praxis hin oder her 
— ohne solide theoretische Bases ist da nix zu wollen ...

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Nein, GCC arbeitet nicht so, sondern die Makefiles der Anwender.
> Alles, was diese tun müssten, ist, alle Quellen in einem Aufruf an GCC
> zu übergeben. Bleibt die Frage, wie dann der Compile-Aufruf für FireFox
> oder GCC selbst aussähe...

Das würde ich mich auch fragen. Der Compile-Prozess von GCC ist ja nicht 
gerade übersichtlich. Da bestehen einige Abhängigkeiten. Die Frage wäre 
auch, ob diese Unmenge Daten überhaupt in das RAM passen würde?

Johann L. schrieb:
> Naja, aber mindesten 95%. Was gespart wird, ist Lexen und Parsen, viel
> mehr nicht. Alles, was danach kommt und schon einmal mühsam in mehreren
> 100 Passes zusammengetragen wurde, ist Makulatur und muss nochmals
> gemacht werden, zuzüglich File I/O und (De)Serialisierung.

Wäre es umsetzbar im Falle von LTO den Vorgang an der Stelle bereits 
abzubrechen, wo die für die eigentliche Kompilierung erforderlichen 
Daten gesammelt sind? Der direkt erzeugte Code in den Objekt-Dateien 
wird ja sowieso verworfen.

Johann L. schrieb:
> Daß LTO keine Optimierung zur Linkzeit ist, sieht man auch daran, daß
> Informationen, die erst dem Linker bekannt sind, nicht zur Optimierung
> per LTO herangezogen werden können.

Ich schrieb ja, dass mit LTO versucht wird, diese Punkte zu 
verbessern. Der Idealzustand sähe natürlich anders aus. Aber dennoch ist 
eine kleine Verbesserung immer noch besser als keine Verbesserung, oder 
wie siehst du das? Sonst wäre wohl kaum so viel Zeit in die Entwicklung 
investiert worden.

Johann L. schrieb:
> Das einzige, was in final noch gemacht wird, sind Text-Peepholes
> (deprecated) und cc0-Behandlung (deprecated). Wenn mich nicht alles
> täuscht, ist avr das einzige Target, das noch cc0 verwenden um
> ConditionCode abzubilden. Der Umbau von cc0 zu CCmode wäre allerdings
> gewaltig und brächte zunächst nichts ausser ein Sack voll neuer Bugs.

Bedeutet im Umkehrschluss aber auch, dass mit Veränderungen in solchen 
Teilen nicht mehr zu rechnen ist, wenn diese keine weitere Anwendung 
mehr haben außer bei dem AVR-Target, oder?

Johann L. schrieb:
> Ich erlebe 3.4.6 als die stabilste Version überhaupt. Allerdings
> verwende ich die nur als WinAVR, also mit Patches. Ne naked 3.4.6 hatte
> ich nie im Einsatz.

Ja, genau die habe ich auch. Ich habe mal spaßeshalber versucht, den 
3.4.6 selbst zu kompilieren. Ist mir nicht gelungen, vermutlich sind die 
Abhängigkeiten schon zu modern für das alte Teil, ob ein eigenes 
Kompilat daher besser oder schlechter wäre, kann ich nicht beurteilen.

Johann L. schrieb:
> nennt sich ABI ;-)
>
>> Wenn ein temporäres Register genutzt wird, wird push/pop erzeugt
>> ohne dass dies eventuell nötig wäre. Daran "krankt" das Teil.
>
> Doch, die ABI verlangt es.

Das dieses Kind einen Namen hat, war ja klar. ;-)

Für mich ist das halt nicht der Weisheit letzter Schluss. Musst mir ja 
nicht zu stimmen, wenn du "ABI" für komfortabel und zweckmäßig hältst, 
ich lasse jedem seine Meinung.

Johann L. schrieb:
> Ja, nachher ist man immer schlauer. Nehmen wir an, wir machen eine
> entsprechende Optimierung und reichern die Assemebler-Ausgabe mit
> Anmerkungen an, welche Register wirklich verwendet werden.
>
> Wie stellst du nun sicher, daß b aus Modul b verwendet wird und nicht b
> aus einem anderen Modul c? C erlaubt es: a ist nur das Interface bekannt
> und das verwendere ABI.

Ja, so einfach geht das natürlich auch wieder nicht. Freies verwenden 
der Funktionen kreuz und quer durch die Module geht damit nicht mehr. 
;-)

Das geht mit "ABI" mit "Global Register Coloring" aber nicht. Der C51 
schaut im ersten Schritt nach den Abhängigkeiten, also welche Funktion 
ruft welche andere Funktion auf usw. Das merkt er sich dann in einer 
Datei. Nun beginnt die eigentliche Kompilierung in der er zunächst, wie 
in der normalen Kompilierung ohne diese Optimierung auch, die Register 
verteilt. Danach schließen sich dann aber solange weitere Durchläufe an, 
bis keine weitere Optimierung der Register mehr möglich ist. Wovon der 
Compiler das abhängig macht, weiß ich nicht. Ist auch nicht 
dokumentiert, vermutlich Betriebsgeheimnis.

Wenn man jetzt relativ kleinere Änderungen an einer Funktion vornimmt 
kann es sein, dass diese Änderung durch Neukompilierung des betreffenden 
Moduls erledigt ist. Der Compiler erkennt dies ja daran, ob sich die 
Abhängigkeiten durch die Änderung ebenfalls geändert haben oder nicht.

Ändert man hingegen größere Dinge kann es sein, dass eine umfangreichere 
Neukompilierung oder sogar ein komplett neuer Durchgang von Anfang an 
erforderlich wird.

Johann L. schrieb:
> Wenn es leicht ist und der Ansatz der allgemein genug, sollte es doch
> kein Problem für die C51-Jungs und -Mädels sein, im Revier der
> AVR-Compiler zu wildern?

Der C51 gehört heute zu ARM. Denen wäre es wohl eher gelegen, wenn Atmel 
mehr Chips mit deren Core verkauft anstelle eines besseren Compilers für 
die Konkurrenz zu entwickeln.

Und dann schätze ich, dass deren Teil alles andere als portabel ist. 
Intern wird wohl Vieles maßgeschneidert für den 8051 ohne größere 
Abstraktion sein. Genau weiß ich es natürlich nicht, es handelt sich ja 
um ein proprietäres und kommerzielles Produkt.

Johann L. schrieb:
> Sond doch schon drinne und hören auf __int24 und __uint24. Das einzige,
> was noch fehlt ist Multiplikation (__mulpsi3) und die *räusper*
> Dokumentation.

Echt? Habe ich noch nie in einem Quellcode gesehen und gelesen habe ich 
davon, glaube ich, auch noch nichts. Wie werden die an Funktionen 
übergeben? Wie ein 32-Bitter nur mit einem Byte Verschwendung?

Johann L. schrieb:
> hä? libquadmath ist doch nur ne Lib wie libgcc auch? Die sind doch fix
> durch...

Das sagst du! Bei mir dauert das einerseits für die Lib, dann für 
interne Angelegenheiten des GCC selbst und schließlich für jede 
AVR-Variante (also "avr2", "avr25", "avr3" usw.). Das quält sich ewig 
lange herum. Zwischendrin kommen massenhaft Warnungen und andere 
Hinweise.

Johann L. schrieb:
> Nicht abschalten, sondern nen gscheiten Bug-Report machen!
> War wahrscheinlich ein guter, alter Spill Failure?

Ja, "unable to find a register to spill", aber auch "Segmentation 
Fault". Wenn man versucht diesen Sachen etwas auf den Grund zu gehen, 
verläuft es sich meistens in reload1.c. Für die Bugreports kenne ich 
mich zu wenig mit diesen Sachen aus. Man müsste ja auch einen 
reproduzierbaren Codeschnippsel beifügen usw.

Mir ist noch etwas eingefallen:

Bisher hat -mcall-prologues immer gute Dienste geleistet. Seit Version 
4.7 werden die Register mitunter aber nicht mehr immer zusammenhängend 
genutzt. Die Folge ist, dass der Compiler nicht die beiden Calls 
erzeugt, sondern selbst wieder PUSH/POP zusammenstellt. Die Folge ist 
dann natürlich wieder ein größerer Code.

Hier wäre es sicher sinnvoller zwei, drei Register zu viel zu sichern, 
anstelle dreißig, vierzig Bytes Code pro Funktion zu erzeugen. Ist dir 
dies auch bereits aufgefallen?

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


Lesenswert?

Johann L. schrieb:
> Wie die Leute ihre Makefiles schreiben, hat ja nix direkt mit
> Sprachstandards zu tun, sondern sie wollen einfach Zeit sparem beim
> (mehrfachen) Compilieren.

Es ist mehr als die Makefiles der Nutzer, Johann.  Bibliotheken
können ja auch noch mit dazu kommen.  Wenn jeder in seine Code noch
den kompletten Sourcecode der libc mit ablegen sollte, wären die
Leute auch kaum glücklich. ;-)  (Klappt natürlich ohnehin nur für
statisch gelinkte Bibliotheken, dynamische lassen sich auf diese
Weise nicht behandeln.)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Naja, aber mindesten 95%. Was gespart wird, ist Lexen und Parsen, viel
>> mehr nicht. Alles, was danach kommt und schon einmal mühsam in mehreren
>> 100 Passes zusammengetragen wurde, ist Makulatur und muss nochmals
>> gemacht werden, zuzüglich File I/O und (De)Serialisierung.
>
> Wäre es umsetzbar im Falle von LTO den Vorgang an der Stelle bereits
> abzubrechen, wo die für die eigentliche Kompilierung erforderlichen
> Daten gesammelt sind? Der direkt erzeugte Code in den Objekt-Dateien
> wird ja sowieso verworfen.

Umsetzbar bestimmt, GCC ist immer nochz Menschen- und nicht Hexenwerk 
;-)

Vielleicht gibt's sowas wie -flto-only ja schon, ich kenn mich mit LTO 
nicht aus. Sinnvoll erscheint mir sowas auf jeden Fall und es würde den 
Resourcenverbrauch deutlich reduzieren wenn der Anwender ohnehin weiß, 
daß er LTO verwenden wird zur Linkzeit.

Einfach mal in gcc-help@ oder so nachfragen.

> Johann L. schrieb:
>> Das einzige, was in final noch gemacht wird, sind Text-Peepholes
>> (deprecated) und cc0-Behandlung (deprecated). Wenn mich nicht alles
>> täuscht, ist avr das einzige Target, das noch cc0 verwendt um
>> ConditionCode abzubilden. Der Umbau von cc0 zu CCmode wäre allerdings
>> gewaltig und brächte zunächst nichts ausser ein Sack voll neuer Bugs.
>
> Bedeutet im Umkehrschluss aber auch, dass mit Veränderungen in solchen
> Teilen nicht mehr zu rechnen ist, wenn diese keine weitere Anwendung
> mehr haben außer bei dem AVR-Target, oder?

Wenn man zwischen den Zeilen liest: Ja. Am liebsten würde man 
das AVR-Backend gründlich aufräumen und das ganzen cc0-Zeug entsorgen — 
auch das aus dem Middle-End.

> Johann L. schrieb:
>> Ich erlebe 3.4.6 als die stabilste Version überhaupt. Allerdings
>> verwende ich die nur als WinAVR, also mit Patches. Ne naked 3.4.6 hatte
>> ich nie im Einsatz.
>
> Ja, genau die habe ich auch. Ich habe mal spaßeshalber versucht, den
> 3.4.6 selbst zu kompilieren. Ist mir nicht gelungen, vermutlich sind die
> Abhängigkeiten schon zu modern für das alte Teil, ob ein eigenes
> Kompilat daher besser oder schlechter wäre, kann ich nicht beurteilen.

Den i386-mingw32-gcc, den ich für Canadian Cross verwende, zeigt als 
Version 3.4.5. Und mit Überstzen von 4.7 hat der keine Probleme. Ausser 
aben dem LTO-Zeug, was aber nicht an dem Cross-Build GCC liegt, sondern 
an der Umgebung die vorausgesetzt wird (libdl).

> Johann L. schrieb:
>> nennt sich ABI ;-)
>>
>>> Wenn ein temporäres Register genutzt wird, wird push/pop erzeugt
>>> ohne dass dies eventuell nötig wäre. Daran "krankt" das Teil.
>>
>> Doch, die ABI verlangt es.
>
> Das dieses Kind einen Namen hat, war ja klar. ;-)
>
> Für mich ist das halt nicht der Weisheit letzter Schluss. Musst mir ja
> nicht zustimmen, wenn du "ABI" für komfortabel und zweckmäßig hältst,
> ich lasse jedem seine Meinung.

Das ABI (Application Binary Interface) ist eben die Basis, daß 
unterschiedliche Objekte (Assembler, C, C++, ...) überhaupt miteinander 
können. Ein Aufweichen des ABI findet zB bei Function Cloning statt; 
allerdings werden da komplett neue (lokale) Inkarnationen einer Funktion 
angelegt, so daß 100% sicher ist, daß auch genau diese Version verwendet 
wird. Nach aussen hin sind diese Funktionen weder verwend- nocht 
sichtbar.

Allerdings hab ich mich auch schon gefragt, warum diese Optimierung 
nicht gemacht wird. Die Schwierigkeiten und Schweinereien, denen man 
sich dabei gegenübersieht, sind allerdings nicht zu unterschätzen. Auch 
hier könnte ein Nachfragen in gee-help@ oder gcc@ interessante neue 
Gesichtspuunkte bringen — auch in die Semantik von C und den Linkprozess 
als solchen.

Ein Grund, warum es nicht angegangen wurde, dürfte auch die 
Intel/x86-zentriertheit sein:  Auf einer Maschine, die praktisch keine 
Register hat und alles auf dem Speicher/Stack macht, muss man sich um 
sowas eben keinen Kopf machen...

> Johann L. schrieb:
>> Ja, nachher ist man immer schlauer. Nehmen wir an, wir machen eine
>> entsprechende Optimierung und reichern die Assemebler-Ausgabe mit
>> Anmerkungen an, welche Register wirklich verwendet werden.
>>
>> Wie stellst du nun sicher, daß b aus Modul b verwendet wird und nicht b
>> aus einem anderen Modul c? C erlaubt es: a ist nur das Interface bekannt
>> und das verwendere ABI.
>
> Ja, so einfach geht das natürlich auch wieder nicht. Freies Verwenden
> der Funktionen kreuz und quer durch die Module geht damit nicht mehr.
> ;-)

Was bedeutet "geht nicht mehr?" Der Anwender ist dafür verantwortlich? 
Schlecht, denn er macht Fehler und verflucht die Tools. Die Tools? Wie 
stellst du das sicher?

> Johann L. schrieb:
>> Sind doch schon drinne und hören auf __int24 und __uint24. Das einzige,
>> was noch fehlt ist Multiplikation (__mulpsi3) und die *räusper*
>> Dokumentation.
>
> Echt?

Ja. Echt.

> Habe ich noch nie in einem Quellcode gesehen

Schreib nen Quellcode, dann siehst du ihn auch ;-)

> und gelesen habe ich davon, glaube ich, auch noch nichts.
> Wie werden die an Funktionen übergeben?
> Wie ein 32-Bitter nur mit einem Byte Verschwendung?

Ja. Intern werden 3-Byte Werte mit geraden Registern beginnend abgelegt. 
Vielleicht sollte das noch geändert werden? Gewinnen tut man dadurch ja 
nix, es macht nur die Registerallokierung schwerer...

> Johann L. schrieb:
>> Nicht abschalten, sondern nen gscheiten Bug-Report machen!
>> War wahrscheinlich ein guter, alter Spill Failure?
>
> Ja, "unable to find a register to spill", aber auch "Segmentation
> Fault". Wenn man versucht diesen Sachen etwas auf den Grund zu gehen,
> verläuft es sich meistens in reload1.c. Für die Bugreports kenne ich
> mich zu wenig mit diesen Sachen aus. Man müsste ja auch einen
> reproduzierbaren Codeschnippsel beifügen usw.

Für einen BugREPORT brauchst du weder in den Quellen zu stochern noch 
Lösungsansätze oder gar Patches.

Wenn dir sowas um die Ohren fliegt, hast du doch direkt die Zeile auf 
der Console stehen, die crasht. Als erstes isoliert man den 
Compileraufruf, so daß man einen direkten Aufruf hat, also einen der 
nicht von make veranstaltet wird. Dann macht man -save-temps -v zum 
Aufruf hinzu ist ist i.W. fertig, siehe

http://gcc.gnu.org/bugs/#need

Was den Entwicklern enorm Arbeit spart, ist einen möglichst kleinen 
Testfall zu finden; aber für einen Bugreport ist das nicht nötig. Einen 
klenen Testfall zu finden kann manchmal ganz schön Arbeit sein, aber 
wenn man ihn hat, ist die Lösung natürlich auch viel einfacher zu finde; 
Beispiel PR39633, der ~10 Jahre im Compiler war, bis er überhaupt 
bemerkt und behoben wurde:
1
char c;
2
3
void func_1 (char a)
4
{
5
    a = a >> 7;
6
    if (a)
7
        c = a;
8
}

> Mir ist noch etwas eingefallen:
>
> Bisher hat -mcall-prologues immer gute Dienste geleistet. Seit Version
> 4.7 werden die Register mitunter aber nicht mehr immer zusammenhängend
> genutzt. Die Folge ist, dass der Compiler nicht die beiden Calls
> erzeugt, sondern selbst wieder PUSH/POP zusammenstellt. Die Folge ist
> dann natürlich wieder ein größerer Code.

Vielleicht verbessert der Fix für PR50775 die Gemängelage?
Oder -fira-algorithm=priority?

> Hier wäre es sicher sinnvoller zwei, drei Register zu viel zu sichern,
> anstelle dreißig, vierzig Bytes Code pro Funktion zu erzeugen. Ist dir
> dies auch bereits aufgefallen?

Momentan werden die zu sichernden Register von der Live_Info abgeleitet, 
siehe ./gcc/config/avr/avr.c:expand_prologue und expand_expilogue.

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:

> Ein Grund, warum es nicht angegangen wurde, dürfte auch die
> Intel/x86-zentriertheit sein:  Auf einer Maschine, die praktisch keine
> Register hat und alles auf dem Speicher/Stack macht,

Da sich mindestens die immobile Gerätewelt schon heute deutlich in 
Richtung real als solche genutzter 64-Bit Prozessoren bewegt, und die 
mobile weg von x86, ist dessen eklatante Registerarmut immer mehr Schnee 
von gestern.

AMD64 und ARM haben mehr davon. Ok, mit IA64 können sich die trotzdem 
nicht messen. ;-)

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Umsetzbar bestimmt, GCC ist immer nochz Menschen- und nicht Hexenwerk
> ;-)
>
> Vielleicht gibt's sowas wie -flto-only ja schon, ich kenn mich mit LTO
> nicht aus. Sinnvoll erscheint mir sowas auf jeden Fall und es würde den
> Resourcenverbrauch deutlich reduzieren wenn der Anwender ohnehin weiß,
> daß er LTO verwenden wird zur Linkzeit.
>
> Einfach mal in gcc-help@ oder so nachfragen.

Wenn es so etwas schon gäbe, dann höchstens genauso undokumentiert wie 
die 24 Bit-Integer. Ich werde also nochmals recherchieren und falls ich 
nichts finde, an den Support schreiben.

Aber: Gib LTO mal eine Chance und schau dir dann einmal realen Code an 
und nicht nur Testcases oder dergleichen. Schaden kann es keinen 
anrichten.

Johann L. schrieb:
> Wenn man zwischen den Zeilen liest: Ja. Am liebsten würde man
> das AVR-Backend gründlich aufräumen und das ganzen cc0-Zeug entsorgen —
> auch das aus dem Middle-End.

Dann hoffen wir mal, dass nichts dem Mainstream in die Quere kommt...

Johann L. schrieb:
> Den i386-mingw32-gcc, den ich für Canadian Cross verwende, zeigt als
> Version 3.4.5. Und mit Überstzen von 4.7 hat der keine Probleme. Ausser
> aben dem LTO-Zeug, was aber nicht an dem Cross-Build GCC liegt, sondern
> an der Umgebung die vorausgesetzt wird (libdl).

Mein GCC aus MinGW hat schon Version 4.5.2 vielleicht liegt es daran? 
Ich weiß gar nicht ob so ein altes MinGW mit Windows 7 noch 
zurechtkommt. Da gab es, glaube ich, auch anfangs erst Probleme.

Johann L. schrieb:
> Das ABI (Application Binary Interface) ist eben die Basis, daß
> unterschiedliche Objekte (Assembler, C, C++, ...) überhaupt miteinander
> können. Ein Aufweichen des ABI findet zB bei Function Cloning statt;
> allerdings werden da komplett neue (lokale) Inkarnationen einer Funktion
> angelegt, so daß 100% sicher ist, daß auch genau diese Version verwendet
> wird. Nach aussen hin sind diese Funktionen weder verwend- nocht
> sichtbar.

Für das Mischen von Objekten, die aus unterschiedlichen 
Programmiersprachen resultieren, braucht man kein ABI. Die Frontends 
erzeugen doch immer einen "genormten" Syntaxtree, welches das Middleend 
dann weiterverarbeitet und schließlich dem für das gewählte Target 
erforderlichem Backend übergibt, oder?

Das Register Coloring findet ja nicht auf einer hochsprachlichen Ebene 
statt, sondern erst auf maschinensprachlichem Niveau.

Johann L. schrieb:
> Allerdings hab ich mich auch schon gefragt, warum diese Optimierung
> nicht gemacht wird. Die Schwierigkeiten und Schweinereien, denen man
> sich dabei gegenübersieht, sind allerdings nicht zu unterschätzen. Auch
> hier könnte ein Nachfragen in gee-help@ oder gcc@ interessante neue
> Gesichtspuunkte bringen — auch in die Semantik von C und den Linkprozess
> als solchen.

Freut mich, dass du diese Art der Optimierung nicht verteufelst und mich 
überhaupt schon so lange mit meinen kritischen Beiträgen erträgst. ;-)

Ich finde den C51 zwar, nicht zuletzt durch diese Optimierung, 
fantastisch. Aber ich ginge ja noch weiter! Würde man einen modernen 
C-Compiler so aufbauen wie beispielsweise einen Java- oder C#-Compiler, 
so bräuchte man noch nicht einmal mehr Headerfiles.

Das man solche Compiler in GCC integrieren könnte, sieht man am Beispiel 
Java. Java wird ja auch "irgendwie" im GCC unterstützt - ausprobiert 
habe ich dies aber noch nicht selbst. Wobei Java eher wieder ein 
schlechtes Beispiel ist, weil hier aus jeder ".java"-Datei eine 
getrennte ".class"-Datei erzeugt wird. Der C#-Compiler hingegen bekommt 
alle Quellcodedateien auf einmal und erzeugt daraus genau eine Assembly 
(ob die EXE oder DLL heißt ist dabei egal, es geht beides).

Wenn ein C-Compiler ebenfalls alle Module auf einmal verarbeiten würde, 
wüsste er, was in welchen Modul definiert ist und bräuchte daher keine 
externen Deklarationen mehr. Das wäre doch mal was, oder? Gleichzeitig 
könnte das Teil natürlich nach wie vor die althergebrachte 
Headerfile/Sourcefile-Geschichte unterstützen. Wenn der Compiler die 
Definition schon kennt, kann er ja über eine redundante externe 
Deklaration hinwegsehen.

Bevor Jörg kontert: Libraries müssten dann natürlich auch sich 
selbstbeschreibende Metadaten mitbringen. Steht aber doch sowieso in den 
Debuginfos, oder? Die ELF-Dateien sind so groß, da steht bestimmt nicht 
nur Code drin. Ansonsten bräuchte es hier halt weiterhin das Headerfile.

Würde ich das direkt an GCC vorschlagen, würden die mich für vermutlich 
verrückt halten, oder was meinst du?

C, und natürlich auch C++, würde dadurch bequemer werden. Die Meldung, 
dass die Definition nicht mehr zur Deklaration passt, erhalte ich 
zumindest fast täglich. Gerne übersieht man so etwas.

Johann L. schrieb:
> Ein Grund, warum es nicht angegangen wurde, dürfte auch die
> Intel/x86-zentriertheit sein:  Auf einer Maschine, die praktisch keine
> Register hat und alles auf dem Speicher/Stack macht, muss man sich um
> sowas eben keinen Kopf machen...

Eigentlich jedes Handy hat heute einen ARM. Die haben Register - mit 
denen arbeite ich auch. An dem GCC für den ARM bemängele ich vieles von 
dem, was ich auch am avr-gcc bemängele. Aber so ist das halt, wenn alles 
aus einem Guss ist.

Johann L. schrieb:
> Was bedeutet "geht nicht mehr?" Der Anwender ist dafür verantwortlich?
> Schlecht, denn er macht Fehler und verflucht die Tools. Die Tools? Wie
> stellst du das sicher?

Nein, natürlich ist der Anwender dafür nicht verantwortlich. Ich habe 
mich zu unpräzise ausgedrückt. Der C51 macht das alles automatisch, du 
kannst natürlich jeden beliebigen, korrekten, Code schreiben. Mit "geht 
nicht mehr" meinte ich vielmehr, dass nachdem bereits Module kompiliert 
wurden, nicht mehr alles möglich ist, ohne diese Module erneut 
kompilieren zu müssen, da durch die Optimierung Abhängigkeiten 
entstehen.

Auch dies geschieht automatisch. Wie bereits geschrieben, hat der 
Compiler eine Tabelle erzeugt, was von was abhängig ist. Anhand dieser 
Daten wird entschieden, was erneut, im Falle einer Änderung, zu 
kompilieren ist. Ändert du in einem Projekt mit zehn Modulen also in 
einem Modul eine Kleinigkeit, kann es sein, dass fünf andere Module 
ebenfalls neukompiliert werden müssen.

Ist dir jetzt klarer, wie ich es meinte?

Johann L. schrieb:
> Ja. Intern werden 3-Byte Werte mit geraden Registern beginnend abgelegt.
> Vielleicht sollte das noch geändert werden? Gewinnen tut man dadurch ja
> nix, es macht nur die Registerallokierung schwerer...

Nun ja, ein 8 Bit-Integer würde da noch reinpassen. Das dürfte auf dem 
AVR der häufigste Datentyp sein! Bedenke auch, dass es Funktionen gibt, 
die mehr als zwei, drei Parameter haben können. Dank "ABI" läuft das ja 
sonst auf eine halbe Katastrophe hinaus, da der Compiler nur einen 
Bruchteil der vorhandenen Register nutzen darf und der Rest dann mittels 
Stack, "Frame Pointer" und jeder Menge anderem Overhead übertragen wird.

Johann L. schrieb:
> Für einen BugREPORT brauchst du weder in den Quellen zu stochern noch
> Lösungsansätze oder gar Patches.
>
> Wenn dir sowas um die Ohren fliegt, hast du doch direkt die Zeile auf
> der Console stehen, die crasht. Als erstes isoliert man den
> Compileraufruf, so daß man einen direkten Aufruf hat, also einen der
> nicht von make veranstaltet wird. Dann macht man -save-temps -v zum
> Aufruf hinzu ist ist i.W. fertig, siehe
>
> http://gcc.gnu.org/bugs/#need

Gut, wenn mir zukünftig so etwas erneut passiert, schreibe ich einen 
Eintrag in die "Höhle des Löwen".

Johann L. schrieb:
> Was den Entwicklern enorm Arbeit spart, ist einen möglichst kleinen
> Testfall zu finden; aber für einen Bugreport ist das nicht nötig. Einen
> klenen Testfall zu finden kann manchmal ganz schön Arbeit sein, aber
> wenn man ihn hat, ist die Lösung natürlich auch viel einfacher zu finde;
> Beispiel PR39633, der ~10 Jahre im Compiler war, bis er überhaupt
> bemerkt und behoben wurde:

Dein Wort... ;-)

Johann L. schrieb:
> Vielleicht verbessert der Fix für PR50775 die Gemängelage?
> Oder -fira-algorithm=priority?

Werde ich ausprobieren, genau so wie anderen Dinge von oben. Heute 
Nachmittag hatte ich angefangen mir einen aktuellen avr-gcc zu basteln. 
Dann kam aber leider etwas berufliches dazwischen... (Nein, weder AVR 
noch ARM: dsPIC.)

Johann L. schrieb:
> Momentan werden die zu sichernden Register von der Live_Info abgeleitet,
> siehe ./gcc/config/avr/avr.c:expand_prologue und expand_expilogue.

Könnte man hier Anpassungen vornehmen, dass notfalls ein paar unnötige 
Register mitgesichert werden um stattdessen PUSH/POP einzusparen? 
Eventuell mit einer Option:

-mstrict-call-prologues?

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


Lesenswert?

Lars R. schrieb:
> Bevor Jörg kontert: Libraries müssten dann natürlich auch sich
> selbstbeschreibende Metadaten mitbringen. Steht aber doch sowieso in den
> Debuginfos, oder? Die ELF-Dateien sind so groß, da steht bestimmt nicht
> nur Code drin.

Bislang kann man sie zumindest von den Debugsymbolen befreien (dann
reduziert sich die Größe auf ein Fünftel bis ein Zehntel), und sie
lassen sich trotzdem linken, solange die Symboltabelle selbst noch
drin bleibt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> hä? libquadmath ist doch nur ne Lib wie libgcc auch? Die sind doch fix
>> durch...
>
> Das sagst du! Bei mir dauert das einerseits für die Lib, dann für
> interne Angelegenheiten des GCC selbst und schließlich für jede
> AVR-Variante (also "avr2", "avr25", "avr3" usw.).

Jörg ist am überlegen, für jedes Derivat eine eigene multilib zu 
erzeugen. Gibt dann irgendwas in der Größenordnung 10²...10³ multilibs 
;-)

Lars R. schrieb:
> Johann L. schrieb:
>>
>> Vielleicht gibt's sowas wie -flto-only ja schon, [...]
>> Einfach mal in gcc-help@ oder so nachfragen.
>
> Wenn es so etwas schon gäbe, dann höchstens genauso undokumentiert wie
> die 24 Bit-Integer.

Daß die (noch) nicht dokumentiert sind, liegt einfach am Zeitmangel. 
AUsserdem tut's keinem weh. Was nicht verwendet wird, produziert auch 
keine Bugreports :o)

> Johann L. schrieb:
>> Das ABI (Application Binary Interface) ist eben die Basis, daß
>> unterschiedliche Objekte (Assembler, C, C++, ...) überhaupt miteinander
>> können. Ein Aufweichen des ABI findet zB bei Function Cloning statt;
>> allerdings werden da komplett neue (lokale) Inkarnationen einer Funktion
>> angelegt, so daß 100% sicher ist, daß auch genau diese Version verwendet
>> wird. Nach aussen hin sind diese Funktionen weder verwend- nocht
>> sichtbar.
>
> Für das Mischen von Objekten, die aus unterschiedlichen
> Programmiersprachen resultieren, braucht man kein ABI.

*hüstel* Wie Bitte? Das ABI legt zB fest, in welchen Registern welche 
Werte übergeben werden. Soll der Compiler per Zufallsgenerator 
entscheiden, in welchen Registern er welche Werte übergibt, zB bei
1
int bar (long (*foo)(char, long long))
2
{
3
    return foo (1, 2);
4
}
Weiters wird Endianess festgelegt, wie groß int, short und long sind, 
etc.

> Die Frontends erzeugen doch immer einen "genormten" Syntaxtree,
> welches das Middleend dann weiterverarbeitet und schließlich dem
> für das gewählte Target erforderlichem Backend übergibt, oder?

Ja, und?

> Das Register Coloring findet ja nicht auf einer hochsprachlichen Ebene
> statt, sondern erst auf maschinensprachlichem Niveau.

Ich versteh den Zusammenhang immernoch nicht.

> Johann L. schrieb:
>> Allerdings hab ich mich auch schon gefragt, warum diese Optimierung
>> nicht gemacht wird. Die Schwierigkeiten und Schweinereien, denen man
>> sich dabei gegenübersieht, sind allerdings nicht zu unterschätzen. Auch
>> hier könnte ein Nachfragen in gee-help@ oder gcc@ interessante neue
>> Gesichtspuunkte bringen — auch in die Semantik von C und den Linkprozess
>> als solchen.
>
> Freut mich, dass du diese Art der Optimierung nicht verteufelst und mich
> überhaupt schon so lange mit meinen kritischen Beiträgen erträgst. ;-)
>
> Ich finde den C51 zwar, nicht zuletzt durch diese Optimierung,
> fantastisch. Aber ich ginge ja noch weiter! Würde man einen modernen
> C-Compiler so aufbauen wie beispielsweise einen Java- oder C#-Compiler,
> so bräuchte man noch nicht einmal mehr Headerfiles.

Das ist keine Sache des Compilers, sondern der Sprache.
Und auch C kannst du komplett ohne Header un #include schreiben. Ob das 
ein Gewinn ist, sei dahingestellt.

> Wenn ein C-Compiler ebenfalls alle Module auf einmal verarbeiten würde,
> wüsste er, was in welchen Modul definiert ist und bräuchte daher keine
> externen Deklarationen mehr.

Wie gesagt, wenn du keine Header mehr willst, verbacke alles in einem 
amorphen Quellklumpen und fertig ist.

> Bevor Jörg kontert: Libraries müssten dann natürlich auch sich
> selbstbeschreibende Metadaten mitbringen.

Header?

> Steht aber doch sowieso in den Debuginfos, oder?

Was Debug-Info damit zu tun?

> Würde ich das direkt an GCC vorschlagen, würden die mich für vermutlich
> verrückt halten, oder was meinst du?

Vorschlagen kannst du alles mögliche und unmögliche. Aber:

Die Leute, die GCC entwickeln, haben Arbeit bis Oberkante Unterkiefer, 
und daß sie aufgrund eines solchen Vorschlags alles stehen und liegen 
lassen, und sich nur noch darum kümmern, ist nicht sooo wahrscheinlich. 
Zumal das Kosten-Nutzen-Verhältnis gewaltig ist und sowas nicht eben mal 
so und nicht in ein paar paar Wochen in GCC reinzuklimpern ist.

Ausserdem kannst du bei GCC nicht wie bei einem hausbackenen Compiler 
hingehen, und Tools wie make und Compiler und Assembler und Linker und 
facebook und wer weiß was noch alles zu einem riesigen Batzen 
verwursten, nur um die Info, die gerade gebraucht wird, an der richtigen 
Stelle zu haben, oder weil durch diese Optimierung eine Verflechtung von 
Abhängigkeiten ensteht, die du anderwärtig nicht mehr in der Lage bist 
zu kontrollieren.

> C, und natürlich auch C++, würde dadurch bequemer werden. Die Meldung,
> dass die Definition nicht mehr zur Deklaration passt, erhalte ich
> zumindest fast täglich. Gerne übersieht man so etwas.

Du hast ein Qualitätsproblem mit deiner Software?

> An dem GCC für den ARM bemängele ich vieles von dem, was ich auch
> am avr-gcc bemängele.

Dann mach die Bugreports gegen arm-gcc, da sind mehr Entwickler zugange.
...oder waren es zumindest, als codesourcery noch mitmischte.

> Johann L. schrieb:
>> Was bedeutet "geht nicht mehr?" Der Anwender ist dafür verantwortlich?
>> Schlecht, denn er macht Fehler und verflucht die Tools. Die Tools? Wie
>> stellst du das sicher?
>
> Nein, natürlich ist der Anwender dafür nicht verantwortlich. Ich habe
> mich zu unpräzise ausgedrückt. Der C51 macht das alles automatisch, du
> kannst natürlich jeden beliebigen, korrekten, Code schreiben. Mit "geht
> nicht mehr" meinte ich vielmehr, dass nachdem bereits Module kompiliert
> wurden, nicht mehr alles möglich ist, ohne diese Module erneut
> kompilieren zu müssen, da durch die Optimierung Abhängigkeiten
> entstehen.

Siehe "zu einem riesigen Batzen verwursten" von oben.

> Auch dies geschieht automatisch. Wie bereits geschrieben, hat der
> Compiler eine Tabelle erzeugt, was von was abhängig ist. Anhand dieser
> Daten wird entschieden, was erneut, im Falle einer Änderung, zu
> kompilieren ist. Ändert du in einem Projekt mit zehn Modulen also in
> einem Modul eine Kleinigkeit, kann es sein, dass fünf andere Module
> ebenfalls neukompiliert werden müssen.
>
> Ist dir jetzt klarer, wie ich es meinte?

Ja. Du willt einen riesigen, verwursteten Batzen ;-)

> Johann L. schrieb:
>> Intern werden 3-Byte Werte mit geraden Registern beginnend abgelegt.
>> Vielleicht sollte das noch geändert werden? Gewinnen tut man dadurch ja
>> nix, es macht nur die Registerallokierung schwerer...
>
> Nun ja, ein 8 Bit-Integer würde da noch reinpassen.

Wo reinpassen? Es geht um die Ablage in Registern, nicht um 
Parameterübergabe.

> Bedenke auch, dass es Funktionen gibt,
> die mehr als zwei, drei Parameter haben können. Dank "ABI" läuft das ja
> sonst auf eine halbe Katastrophe hinaus, da der Compiler nur einen

Die Katastrophe fängt an, wenn Leute 64-Bit-Typen auf nem 8-Bit µC 
verwenden und mit printf um sich werfen wie auf nem Boliden.

> Johann L. schrieb:
>> Momentan werden die zu sichernden Register von der Live-Info abgeleitet,
>> siehe ./gcc/config/avr/avr.c:expand_prologue und expand_epilogue.
>
> Könnte man hier Anpassungen vornehmen, dass notfalls ein paar unnötige
> Register mitgesichert werden um stattdessen PUSH/POP einzusparen?

Klar, anpassen kasst du alles, es ist Open Source! Wo der Meißel 
anzusetzen ist, steht oben.

Sowas wird von dem beigetragen, dem es wichtig genug ist.
Oder von jemandem, der sein Geld damit verdient.

Wenn du aber mal hochrechnest, wieviele Minuten du einen professionellen 
GCC-Entwickler mit deiner Portokasse bei Laune halten kannst, wirst du 
feststellen, daß die paar Bytes garnicht mehr sooo schlimm sind und du 
lieber beim altvertrauten Preis-Leistungs-Verhältnis von avr-gcc bleibst 
;-)

...oder doch die Internals weiterlesen...?

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


Lesenswert?

Johann L. schrieb:
> Jörg ist am überlegen, für jedes Derivat eine eigene multilib zu
> erzeugen. Gibt dann irgendwas in der Größenordnung 10²...10³ multilibs
> ;-)

Wobei ich deshalb nicht 10² libgcc.a's brauche.  Aber im Moment sieht
es ja so aus, als würden wir eher in Richtung lib${mcu}.a gehen
zuzüglich zur libc.a in der jetzigen Form.  Da ist halt nur das
Problem, wie wir das exakt zwischen einem GCC- und dem zugehörigen
avr-libc-Release koordinieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>> Jörg ist am überlegen, für jedes Derivat eine eigene multilib zu
>> erzeugen. Gibt dann irgendwas in der Größenordnung 10²...10³ multilibs
>> ;-)
>
> Wobei ich deshalb nicht 10² libgcc.a's brauche.  Aber im Moment sieht
> es ja so aus, als würden wir eher in Richtung lib${mcu}.a gehen
> zuzüglich zur libc.a in der jetzigen Form.  Da ist halt nur das
> Problem, wie wir das exakt zwischen einem GCC- und dem zugehörigen
> avr-libc-Release koordinieren.

Unterstütze in-tree build wie die newlib :-)

Und was spricht eigentlich dagegen,

-print-multi-directory
-print-multi-lib
-print-...

auszuwerten?

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


Lesenswert?

Johann L. schrieb:

> Unterstütze in-tree build wie die newlib :-)

Bringt in diesem Zusammenhang rein gar nichts.  Es geht ja dabei
nicht um eine Veränderung der Multilib-Konfiguration, sondern darum,
dass ab einer bestimmten GCC-Version statt -lc dann -lc -l${mcu} -lc
zu linken ist, und dass die avr-libc genau zum gleichen Zeitpunkt so
umgebaut freigegeben werden muss, dass sie auch libc.a und
lib${mcu}.a (statt bisher alles in libc.a) anbietet.  Wenn du das
alles mit Multilibs erschlagen willst, wärst du ja wirklich bei
einer Multilib pro Device, dann bräuchten wir libc.a und lib${mcu}.a
nicht mehr zu trennen, weil es stattdessen N mal ${mcu}/libc.a (und
N mal ${mcu}/libgcc.a etc. pp.) gäbe.  Das hatten wir aber als wenig
sinnvoll in die Ecke gestellt.

> Und was spricht eigentlich dagegen,
>
> -print-multi-directory
> -print-multi-lib
> -print-...
>
> auszuwerten?

Schrieb ich dir schon: dann wären künftig sämtliche avr-libc-
Installationen nur noch mit exakt dem Compiler zu benutzen, mit dem
die Bibliothek selbst compiliert worden war.   OK, vergleichbare
Effekte haben sich teilweise durch __builtin_avr_delay_cycles schon
eingeschlichen, da hatte ich keinen besseren Weg gesehen.  Lieber
wäre es mir gewesen, wenn für derartige neue Features der Präprozessor
einen Makro zusätzlich bekommt, der das Feature anzeigt.

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Jörg ist am überlegen, für jedes Derivat eine eigene multilib zu
> erzeugen. Gibt dann irgendwas in der Größenordnung 10²...10³ multilibs
> ;-)

Wäre auch nicht schlimm, denn ich weiß ja wie ich das ausschalten 
kann...

Johann L. schrieb:
> *hüstel* Wie Bitte? Das ABI legt zB fest, in welchen Registern welche
> Werte übergeben werden. Soll der Compiler per Zufallsgenerator
> entscheiden, in welchen Registern er welche Werte übergibt, zB bei
>
1
> int bar (long (*foo)(char, long long))
2
> {
3
>     return foo (1, 2);
4
> }
5
>
> Weiters wird Endianess festgelegt, wie groß int, short und long sind,
> etc.
>
>> Die Frontends erzeugen doch immer einen "genormten" Syntaxtree,
>> welches das Middleend dann weiterverarbeitet und schließlich dem
>> für das gewählte Target erforderlichem Backend übergibt, oder?
>
> Ja, und?

Ich hüstel auch mal... ;-)

Wo welcher Parameter stehen müsste, muss das C-Frontend ja gar nicht 
wissen. Es reicht wenn der codeerzeugende Teil anhand der Definition 
weiß, wo was stehen muss. Außerdem sind Funktionszeiger ein genereller 
Problemfall! Dafür ist immer eine besondere Behandlung erforderlich. 
Dieses Problem können auch Compiler mit "Register Coloring" nicht 
umgehen.

Die meisten Methodenaufrufe finden bei C aber direkt statt und nicht 
indirekt durch einen Zeiger. Bei C++ mit virtuellen Methoden sähe hier 
die Nutzung von Funktionszeigern natürlich wieder ganz anders aus. Aber 
wir diskutieren hier über den Embeddedbereich.

Wenn der Compiler so arbeitet, wie er gerade arbeitet, dann hast du 
natürlich Recht und ABI ist erforderlich. Diese ABI-Geschichte ist aber 
keine Sache des Hochsprachen-Frontends, daher ist es egal, welche 
Programmiersprache genutzt wird.

Würden die Komponenten des Compilers, welche den eigentlichen Code 
erzeugen, ohne ABI auskommen, zum Beispiel durch andere Herangehensweise 
- notfalls auch mit dem "verwursteten Batzen" - könnte nach wie vor C++ 
mit C und was weiß ich auch immer weiterhin gemischt werden.

Wo habe ich da etwas Falsches geschrieben? Daher schrieb ich, dass ABI 
nicht erforderlich ist um Module verschiedener Programmiersprachen zu 
mischen. Das ist eine reine Frage des Aufbaus der Codeerzeugung an sich.

Johann L. schrieb:
> Das ist keine Sache des Compilers, sondern der Sprache.
> Und auch C kannst du komplett ohne Header un #include schreiben. Ob das
> ein Gewinn ist, sei dahingestellt.

Nein, das kann man nicht. Für den Compiler gibt es keine Headerfiles, 
die sind zu diesem Zeitpunkt bereits "inkludiert". Der C Compiler 
erwartet eine Deklaration bevor man etwas nutzen kann.

Ob ich also in meiner C-Datei oben alle Deklarationen hinschreibe oder 
diese mit in Form einer H-Datei einbinden lasse, ist völlig egal.

Andere Sprachen kommen ohne vorherige Deklaration aus, da ist ein 
Methodenaufruf beispielsweise auch dann erlaubt, wenn die Definition 
erst viel weiter "unten" im Code erscheint. Oder es können auch Methoden 
aus anderen Modulen aufgerufen werden ohne das eine vorherige 
Deklaration notwendig ist.

Daher schrieb ich, dass man einen C-Compiler ebenfalls mit einem solchen 
Arbeitsablauf ausstatten könnte, wie er beispielsweise bei Java oder C# 
realisiert ist.

Johann L. schrieb:
> Wie gesagt, wenn du keine Header mehr willst, verbacke alles in einem
> amorphen Quellklumpen und fertig ist.

Wie oben geschildert, ist dies nicht das, was ich mir gerne wünschen 
würde.

Johann L. schrieb:
> Vorschlagen kannst du alles mögliche und unmögliche. Aber:
>
> Die Leute, die GCC entwickeln, haben Arbeit bis Oberkante Unterkiefer,
> und daß sie aufgrund eines solchen Vorschlags alles stehen und liegen
> lassen, und sich nur noch darum kümmern, ist nicht sooo wahrscheinlich.
> Zumal das Kosten-Nutzen-Verhältnis gewaltig ist und sowas nicht eben mal
> so und nicht in ein paar paar Wochen in GCC reinzuklimpern ist.

Du verstehst etwas grundsätzlich falsch! "Nur" weil ich hier meine Ideen 
aufschreibe, heißt das nicht, dass ich erwarte, dass irgendwer etwas 
tut. Das wäre ja mehr als vermessen.

Irgendwie glaube ich langsam, dass du mich irgendwie für naiv hältst, 
kann das sein?

Johann L. schrieb:
> Ausserdem kannst du bei GCC nicht wie bei einem hausbackenen Compiler
> hingehen, und Tools wie make und Compiler und Assembler und Linker und
> facebook und wer weiß was noch alles zu einem riesigen Batzen
> verwursten, nur um die Info, die gerade gebraucht wird, an der richtigen
> Stelle zu haben, oder weil durch diese Optimierung eine Verflechtung von
> Abhängigkeiten ensteht, die du anderwärtig nicht mehr in der Lage bist
> zu kontrollieren.

Darum schrieb ich ja, dass dies mit GCC aufgrund dessen Arbeitsablaufes 
mit getrenntem Kompilieren, anschließendem Linken usw. nicht möglich 
ist.

Johann L. schrieb:
>> Die Meldung,
>> dass die Definition nicht mehr zur Deklaration passt, erhalte ich
>> zumindest fast täglich. Gerne übersieht man so etwas.
>
> Du hast ein Qualitätsproblem mit deiner Software?

Wie kommst du darauf?

Ich schrieb, dass es einem bei C (oder auch C++) sehr leicht passieren 
kann, dass man beispielsweise einen Datentypen in der Definition ändert, 
aber eine Deklaration übersieht. Der Compiler erkennt diesen Fehler und 
warnt mich deshalb, worauf ich erkenne, eine Stelle im Code übersehen zu 
haben und diese dann ebenfalls richtigstelle.

Dadurch wurde ein Problem bereits behoben, bevor es sich auf die 
Qualität auswirken könnte.

Wobei siehst du hier Qualitätsprobleme? Sag' nicht, dass du immer 
fehlerfreien Code schreibst und dich der Compiler niemals auf einen 
Fehler von dir hinweisen muss. Revision 182058 zeigt mir, dass du 
ebenfalls gerne mal eine Kleinigkeit übersiehst! ;-)

http://gcc.gnu.org/viewcvs/trunk/gcc/config/avr/avr.c?r1=182052&r2=182058&sortby=date&diff_format=h

Du hast ein Komma vergessen, ich vergesse ab und zu ein "u". Wenn 
anstelle von "uint8_t" nur "int8_t" steht, reicht dies aus um 
unerwünschte Resultate zu erhalten. Oder sehe ich das falsch?

Johann L. schrieb:
> Wo reinpassen? Es geht um die Ablage in Registern, nicht um
> Parameterübergabe.

Ja, ich meinte die Parameterübergabe. Geht dies ohne "Lücke"?

Johann L. schrieb:
> Die Katastrophe fängt an, wenn Leute 64-Bit-Typen auf nem 8-Bit µC
> verwenden und mit printf um sich werfen wie auf nem Boliden.

Vielleicht bin ich ja auch gar kein "richtiger" Firmwareentwickler mit 
avr-gcc, wer weiß? Ein paar Beispiele:

 - ich nutze keine Gleitkommazahlen
 - ich nutze nur im allerhöchsten Ausnahmefall mal einen 64 Bit-Typen
 - ich habe noch niemals printf gebraucht, schon gar nicht in Verbindung 
mit Gleitkommazahlen
 - ich mag keine typedef und schreibe daher lieber "struct abc" anstelle 
von "abc_t"

Wenn ich diese (aus meiner Sicht Designfehler) erst regelmäßig benutzen 
muss, um als kompetenten Entwickler wahrgenommen zu werden, verzichte 
ich lieber auf dieses Prädikat! ;-)

Johann L. schrieb:
> Wenn du aber mal hochrechnest, wieviele Minuten du einen professionellen
> GCC-Entwickler mit deiner Portokasse bei Laune halten kannst, wirst du
> feststellen, daß die paar Bytes garnicht mehr sooo schlimm sind und du
> lieber beim altvertrauten Preis-Leistungs-Verhältnis von avr-gcc bleibst
> ;-)
>
> ...oder doch die Internals weiterlesen...?

Ach weißt du, wenn ich Experte auf dem Gebiet des Compilerbaus wäre und 
genügend Zeit hätte, würde ich es sicher bestimmt versuchen besser zu 
machen. Wenn für dich, auch bei der freien Open-Source-Software, nur der 
Gedanke zählt, wie verdiene ich am meisten, dann ist das deine Ansicht. 
Wie gesagt, ich habe hier nur meine Ideen aufgeschrieben, keine Arbeiten 
verteilt. Schade, wenn es für dich anders angekommen ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
>
> Es geht ja dabei nicht um eine Veränderung der
> Multilib-Konfiguration, sondern darum, dass ab einer bestimmten
> GCC-Version statt -lc dann -lc -l${mcu} -lc zu linken ist,
> und dass die avr-libc genau zum gleichen Zeitpunkt so
> umgebaut freigegeben werden muss, dass sie auch libc.a und
> lib${mcu}.a (statt bisher alles in libc.a) anbietet.

Der eigentliche Übelstand ist doch, daß avr-libc keine libc ist, sondern 
viele andere Dinge mitbringt, die nicht in eine libc gehören, "nur" um 
es dem Anwender bequem zu machen und weil es ihm — überspitzt gesagt — 
nicht zuzumuten ist, ein -lfoo anzugeben wenn er ein nicht-C Feature foo 
haben will.

Anfangs war das ganz easy, aber je mehr Devices und Cores es gibt, desto 
schwieriger wird es, diesen Sack voll Flöhe unter den Hut einer libc zu 
bringen.

Aus GCC-Sicht auch ist nicht einzusehen, warum er ein -lfoo ausgeben 
soll, nur weil der Anwender — wieder überspitzt — zu faul ist oder es 
einfach nicht auf den Appel bekommt, neben einem -mmcu=$(TARGET) noch 
ein -l$(TARGET) oder ein -lfoo anzugeben.

Weiters würde avr-gcc dann von der libc anhängig, was nicht durchgehen 
wird, da auch newlib funktionieren muss, siehe RTEMS.

>> Und was spricht eigentlich dagegen,
>>
>> -print-multi-directory
>> -print-multi-lib
>> -print-...
>>
>> auszuwerten?
>
> Schrieb ich dir schon: dann wären künftig sämtliche avr-libc-
> Installationen nur noch mit exakt dem Compiler zu benutzen, mit dem
> die Bibliothek selbst compiliert worden war.

Nicht ganz, sondern: "Eine avr-libc-Installationen ist nur mit
Compilern zu benutzen, die das gleiche ABI implementieren".

Wenn sich aus irgendeinem Grund das ABI ändert, wie zB durch Fix von 
PR51345, wird eine Bibliothek es schwer haben, das 
rauszuparametrisieren.

"Fix von PR51345" bedeutet ein Aufspalten von avr2 und avr25, die bis 
dato munter Devices mit 8-Bit SP und solche mit 16-Bit SP mischen.

Zudem: Eine ABI-Änderung zwischen <= 4.6 und >= 4.7 gibt es ohnehin, sie 
ist nur aus Zeitmangel noch nicht unter "Caveats" in den Release Notes 
veröffentlicht.

Im Klartext: Mit <= 4.6 erzeugte Objekte sind nicht mischbar mit 
Objekten, die mit >= 4.7 erzeugt wurden/werden.

Da es hier ohnehin eine Verwerfung geben wird, ist es sinnvoll, andere 
Umbaumaßnahmen, die sich als notwendig oder überfällig darstellen, 
ebenfalls mit dem Übergang 4.6 -> 4.7 durchzuführen. In avr-libc Zählung 
wäre das dann 1.7 -> 1.8. Oder 2.0?

Da es zumindest im avr-gcc einige Erweiterungen und Neuerungen gab, 
sollte so eine Zäsur durchaus vermittelbar sein.

> OK, vergleichbare Effekte haben sich teilweise durch
> __builtin_avr_delay_cycles schon eingeschlichen,
> da hatte ich keinen besseren Weg gesehen.
> Lieber wäre es mir gewesen, wenn für derartige neue Features der
> Präprozessor einen Makro zusätzlich bekommt, der das Feature anzeigt.

Gibt es, siehe
http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html

> Revision 182058 zeigt mir, dass du
> ebenfalls gerne mal eine Kleinigkeit übersiehst! ;-)
>
> http://gcc.gnu.org/viewcvs/trunk/gcc/config/avr/av...
>
> Du hast ein Komma vergessen, ...

Ja, in letzter Zeit mache ich immer mehr Flüchtigkeitsfehler wie diesen, 
das ist jetzt No 3.

Das, was ich mir da mit avr-gcc selbst aufgehalst habe, ist wohl einfach 
zu viel. Es war einfach eine Illusion zu glauben, all die Bugs, 
Optimierungs-Patzer, fehlenden Features, Aufräumarbeiten, fehlende 
Dokumentation, fehlende Testfälle, bessere Konfigurierbarkeit, benötigte 
Debugmöglichkeiten und weiß der Teufel was sich sonst noch alles über 
die Jahre im avr-gcc angehäuft hat, in meiner Freizeit auch nur 
annähernd bewältigen zu können.

Momentan bin ich echt am zweifeln, ob das überhaupt was bringt und ich 
auch nur annähernd meinen eigenen Qualitätsansprüchen gerecht werden 
kann, oder ob mir die Anwender gründlich den Arsch versohlen, wenn 4.7 
erst offiziell ist.

Und ein einziges Commit wie 182084 macht per Handstreich 100 andere 
Optimierungen im avr-gcc zunichte oder hebelt sie aus, nur weil es auf 
einem ARM besseren Code gibt:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45416#c15

Solche Aktionen sind einfach nur frustrierend, und an allen Ecken und 
quer durch die Foren kann man dann lesen, daß avr-gcc "hier ein 
unnötiges Byte zu viel" und "dort eine missed Optimization, die jeder 
Blinke mit Krückstock binnen 2 Sekunden lösen könnte" und warum "avr-gcc 
so doof ist, dies und jenes nicht zu sehen, was jedem Noob direkt ins 
Auge springt".

> Johann L. schrieb:
>> Wenn du aber mal hochrechnest, wieviele Minuten du einen professionellen
>> GCC-Entwickler mit deiner Portokasse bei Laune halten kannst, wirst du
>> feststellen, daß die paar Bytes garnicht mehr sooo schlimm sind und du
>> lieber beim altvertrauten Preis-Leistungs-Verhältnis von avr-gcc bleibst
>> ;-)
>>
>> ...oder doch die Internals weiterlesen...?
>
> Ach weißt du, wenn ich Experte auf dem Gebiet des Compilerbaus wäre und
> genügend Zeit hätte, würde ich es sicher bestimmt versuchen besser zu
> machen. Wenn für dich, auch bei der freien Open-Source-Software, nur der
> Gedanke zählt, wie verdiene ich am meisten, dann ist das deine Ansicht.

Was ich damit sagen will: Nur weil GCC freie Software ist ("free" as in 
"freedom", not as in "free beer"), bedeutet das nicht, daß er dadurch 
entstand oder vorankommt, weil hier und da mal ein paar Bastler dran 
rumtippsen, denen sonst nix besseres gegen ihre Langeweile einfällt.

GCC-Entwicker arbeiten für Google, IBM, ARM, CodeSourcery, Red Hat, 
SUSE, AMD, Hewlett-Packard.

Leute wie ich, die sich in ihrer Freizeit damit rumbalgen, sind die 
absolute Ausnahmen.

PR51425 ist übrigens behoben.

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


Lesenswert?

Johann L. schrieb:

> Der eigentliche Übelstand ist doch, daß avr-libc keine libc ist, sondern
> viele andere Dinge mitbringt, die nicht in eine libc gehören, ...

Das ist aber gängige Praxis.  Welche C-Bibliothek besitzt denn nur
solche Routinen, die im C-Standard genormt sind?  Mehr oder weniger
jeder wirft da noch einen Haufen anderer Dinge mit rein, um davon zu
profitieren, dass sie auf diese Weise `by default' gleich mit sichtbar
sind.  Auch die historische Trennung von libc.a und libm.a sind doch
mittlerweile eher hinderlich denn nützlich.

Kann sein, dass die newlib da eine Ausnahme ist, aber wenn, dann eher,
weil sie ein halbherzig zusammengewürfeltes Konglomerat diverser
Bruchstücken anderer Bibliotheken (zu großen Teilen von BSD) ist, das
man eigens nur deshalb geschaffen hat, weil dann mal irgendwann klar
war, dass man im Embedded-Bereich mit einer Lizenz wie (L)GPL einfach
keinen Blumentopp gewinnen kann.

> Weiters würde avr-gcc dann von der libc anhängig, was nicht durchgehen
> wird, da auch newlib funktionieren muss, siehe RTEMS.

Dann doch lieber eine Multilib pro Device?

> "Fix von PR51345" bedeutet ein Aufspalten von avr2 und avr25, die bis
> dato munter Devices mit 8-Bit SP und solche mit 16-Bit SP mischen.

Wobei das aus meiner Sicht ein rein kosmetischer Fix ist (und
natürlich eine bessere Optimierung auf den kleinen Devices, weil dann
SPH gar nicht mehr angefasst werden muss), aber die Objekte sind
trotzdem mischbar.

> Im Klartext: Mit <= 4.6 erzeugte Objekte sind nicht mischbar mit
> Objekten, die mit >= 4.7 erzeugt wurden/werden.

Das ist allerdings ungünstig.

> Da es hier ohnehin eine Verwerfung geben wird, ist es sinnvoll, andere
> Umbaumaßnahmen, die sich als notwendig oder überfällig darstellen,
> ebenfalls mit dem Übergang 4.6 -> 4.7 durchzuführen. In avr-libc Zählung
> wäre das dann 1.7 -> 1.8. Oder 2.0?

Wir würden es dann vermutlich lieber 2.0 nennen, denn die major number
war mal dafür reserviert, ein komplett inkompatibles GCC-ABI
anzuzeigen.

(__builtin_avr_delay_cycles)

>> Lieber wäre es mir gewesen, wenn für derartige neue Features der
>> Präprozessor einen Makro zusätzlich bekommt, der das Feature anzeigt.
>
> Gibt es, siehe
> http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html

Danke, das habe ich dann damals entweder übersehen oder aber es war
zu dem Zeitpunkt noch nicht so implementiert.  Ich werde gern das
<util/delay.h> dann wieder ändern.

> Das, was ich mir da mit avr-gcc selbst aufgehalst habe, ist wohl einfach
> zu viel. ...

Ich bin dir außerordentlich dankbar, dass du das tust!

> Solche Aktionen sind einfach nur frustrierend, und an allen Ecken und
> quer durch die Foren kann man dann lesen, daß avr-gcc "hier ein
> unnötiges Byte zu viel" und "dort eine missed Optimization, die jeder
> Blinke mit Krückstock binnen 2 Sekunden lösen könnte" und warum "avr-gcc
> so doof ist, dies und jenes nicht zu sehen, was jedem Noob direkt ins
> Auge springt".

Das sind Leute, die schlicht keinen Schimmer haben, wie ein Compiler
arbeitet, bitte ignorier' sie.

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Der eigentliche Übelstand ist doch, daß avr-libc keine libc ist, sondern
> viele andere Dinge mitbringt, die nicht in eine libc gehören, "nur" um
> es dem Anwender bequem zu machen und weil es ihm — überspitzt gesagt —
> nicht zuzumuten ist, ein -lfoo anzugeben wenn er ein nicht-C Feature foo
> haben will.

Was meinst du konkret? So überladen ist doch die avr-libc gar nicht. Mir 
fallen spontan die Delay-Sachen sowie die CRC-Berechnung ein. Für I2C 
gibt es glaube ich auch noch etwas. Benutzt habe ich außer den Delays 
davon aber selbst noch nichts.

Johann L. schrieb:
> Anfangs war das ganz easy, aber je mehr Devices und Cores es gibt, desto
> schwieriger wird es, diesen Sack voll Flöhe unter den Hut einer libc zu
> bringen.

Muss man unbedingt für jedes einzelne Device Vorkehrungen treffen? Würde 
es nicht ausreichen, die Devices in Gruppen einzuteilen, je nach dem, 
welche Instruktionen (Multiplizieren usw.) vorhanden sind und den Rest 
den Anwender erledigen zu lassen? Momentan gibt die "avr2", "avr3" usw. 
Einteilung dies doch auch her.

Der Anwender müsste sich dann halt selbst darum kümmern, dass der Stack 
richtig passt oder der Code nicht größer wird als der Speicher.

Oder ist der Komfort, wie er derzeit existiert, unbedingt erforderlich? 
Der GCC für ARM erfordert auch wesentlich mehr Mitarbeit (Linker Script, 
Startup Code usw.). Und es gibt dennoch Leute, die auch damit zurecht 
kommen.

Es kann doch nicht Sinn der Sache sein, dass Leute, wie du, in ihrer 
Freizeit alle möglichen Devices eines kommerziellen Anbieters abdecken.

Johann L. schrieb:
> Das, was ich mir da mit avr-gcc selbst aufgehalst habe, ist wohl einfach
> zu viel. Es war einfach eine Illusion zu glauben, all die Bugs,
> Optimierungs-Patzer, fehlenden Features, Aufräumarbeiten, fehlende
> Dokumentation, fehlende Testfälle, bessere Konfigurierbarkeit, benötigte
> Debugmöglichkeiten und weiß der Teufel was sich sonst noch alles über
> die Jahre im avr-gcc angehäuft hat, in meiner Freizeit auch nur
> annähernd bewältigen zu können.

Es ist sicherlich sehr viel. Ich hätte mich da nicht heran gewagt. 
Allerdings finde ich, wie oben geschildert, dass avr-gcc aktuell 
besseren Code erzeugt, als noch einige Versionen zuvor. Von daher denke 
ich, dass es sich doch lohnt, hier nicht aufzugeben.

Johann L. schrieb:
> Momentan bin ich echt am zweifeln, ob das überhaupt was bringt und ich
> auch nur annähernd meinen eigenen Qualitätsansprüchen gerecht werden
> kann, oder ob mir die Anwender gründlich den Arsch versohlen, wenn 4.7
> erst offiziell ist.

An was denkst du hier genau? So schwerwiegende Probleme, dass ein 
Einsatz nicht sinnvoll möglich wäre, sind mir noch nicht untergekommen.

Johann L. schrieb:
> Und ein einziges Commit wie 182084 macht per Handstreich 100 andere
> Optimierungen im avr-gcc zunichte oder hebelt sie aus, nur weil es auf
> einem ARM besseren Code gibt:

Das ist natürlich sehr ärgerlich. Vielleicht wäre es besser, so wie 
Microchip vorzugehen. Die verwalten ihren C30 selbst und übernehmen nur 
die Änderungen bzw. synchronisieren nur soweit, wie es nicht zum 
Nachteil ist.

Kann man nicht einfach frech verlangen, diese Änderung rückgängig zu 
machen wenn niemand im Stande ist, resultierende Probleme bei anderen 
Plattformen korrigieren zu können ohne dass deren Entwicklern 
zusätzlicher Aufwand ensteht?

In der Tat finde ich die Art des "Bugfixings" bei GCC sehr befremdlich. 
Schaut man sich Änderungen, die aus Korrekturen durch Bugmeldungen 
resultieren, genauer an, so fällt auf, dass oftmals nur bei irgendeiner 
"if"-Anweisung in einer Datei mit mehren tausend Zeilen Code zwei oder 
drei weitere Bedingungen angehängt werden. Man kann doch dabei nie im 
Leben alle Eventualitäten abschätzen. Da müssten ja schon Halbgötter am 
Werk sein...

Johann L. schrieb:
> Solche Aktionen sind einfach nur frustrierend, und an allen Ecken und
> quer durch die Foren kann man dann lesen, daß avr-gcc "hier ein
> unnötiges Byte zu viel" und "dort eine missed Optimization, die jeder
> Blinke mit Krückstock binnen 2 Sekunden lösen könnte" und warum "avr-gcc
> so doof ist, dies und jenes nicht zu sehen, was jedem Noob direkt ins
> Auge springt".

Ich schätze mal, dass das Problem keine "Missed Optimization" ist. Damit 
kann man sich ja arrangieren. Das Problem sind hingegen die 
"Regressions" wenn man sieht, dass plötzlich Code schlechter erzeugt 
wird, als es bisher möglich war, so werden sich die Anwender natürlich 
zu Recht beschweren.

Man darf nicht vergessen, dass GCC den Anspruch erhebt für kommerzielle 
Anwendung geeignet zu sein. Du hast selbst geschrieben wer dort alles 
mitmischt. Wenn man so ein Tool professionell einsetzt, dann hat man 
vermutlich einfach eine andere Haltung dem gegenüber als wenn man "bloß" 
irgendein Hobbyprojekt einsetzt!

GCC ist halt ein Riesenteil. Viel mehr freie C-Compiler für AVR fallen 
mir nicht ein. SDCC hat ja den Support eingestellt, bevor er jemals 
richtig ausgereift war. So viel ich gelesen habe, fehlte es dort ja auch 
an den Entwicklern.

Johann L. schrieb:
> Leute wie ich, die sich in ihrer Freizeit damit rumbalgen, sind die
> absolute Ausnahmen.

Aber die machen doch so Projekt sympathisch, meinst du nicht auch?

Ich fände es sehr schade, wenn du damit aufhören würdest. Zumal neben 
dir zurzeit sonst nicht sehr viel passiert.

Jörg Wunsch schrieb:
> Das sind Leute, die schlicht keinen Schimmer haben, wie ein Compiler
> arbeitet, bitte ignorier' sie.

Findest du nicht, dass du hier etwas zu viel pauschalierst? Nur weil 
nicht jeder am dem Teil mit herumschraubt, musst du nicht generell 
sämtliche Kompetenz den Leuten absprechen, die möglicherweise Kritik 
üben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Der eigentliche Übelstand ist doch, daß avr-libc keine libc ist, sondern
>> viele andere Dinge mitbringt, die nicht in eine libc gehören, "nur" um
>> es dem Anwender bequem zu machen und weil es ihm — überspitzt gesagt —
>> nicht zuzumuten ist, ein -lfoo anzugeben wenn er ein nicht-C Feature foo
>> haben will.
>
> Was meinst du konkret? So überladen ist doch die avr-libc gar nicht.

Mein "viele" trifft es auch nicht ganz. Wenn die Features der avr-libc 
es erfordern, die Lib mit Device-Granularität zu erzeugen (bei 150-200 
Devices), dann ist irgendwo der Wurm. Es geht ja nicht um Startup-Code 
oder um Header, sondern wirklich um Library-per-Device.

Warum es zB für ATmega88, ATmega88A, ATmega88P, ATmega88PA jeweils 
eigene Bibliotheken brauchen sollte, will mir nicht so ganz einleuchten.

Und Dinge wie eeprom_* sind doch über Header lösbar?

> Mir fallen spontan die Delay-Sachen sowie die CRC-Berechnung ein.

Die braucht's schon mal nicht auf Device-Ebene.

> Für I2C gibt es glaube ich auch noch etwas.

Ich hab mal überlegt, ob es sinnvoll wäre, RELOCs für SFRs zu haben?
Blöse ist dabei dann aber, daß der I/O-Bereich nicht homogen ist.

> Johann L. schrieb:
>> Anfangs war das ganz easy, aber je mehr Devices und Cores es gibt, desto
>> schwieriger wird es, diesen Sack voll Flöhe unter den Hut einer libc zu
>> bringen.
>
> Muss man unbedingt für jedes einzelne Device Vorkehrungen treffen? Würde
> es nicht ausreichen, die Devices in Gruppen einzuteilen, je nach dem,
> welche Instruktionen (Multiplizieren usw.) vorhanden sind und den Rest
> den Anwender erledigen zu lassen? Momentan gibt die "avr2", "avr3" usw.
> Einteilung dies doch auch her.
>
> Der Anwender müsste sich dann halt selbst darum kümmern, dass der Stack
> richtig passt oder der Code nicht größer wird als der Speicher.
>
> Oder ist der Komfort, wie er derzeit existiert, unbedingt erforderlich?
> Der GCC für ARM erfordert auch wesentlich mehr Mitarbeit (Linker Script,
> Startup Code usw.). Und es gibt dennoch Leute, die auch damit zurecht
> kommen.

Ist wohl so. Bei weniger Comfort, d.h. der Anwender muss selbst was 
machen und was dazulernen, hagelt es erst mal zentnerweise Bug-Reports 
weil "nix mehr geht".

> Johann L. schrieb:
>> Momentan bin ich echt am zweifeln, ob das überhaupt was bringt und ich
>> auch nur annähernd meinen eigenen Qualitätsansprüchen gerecht werden
>> kann, oder ob mir die Anwender gründlich den Arsch versohlen, wenn 4.7
>> erst offiziell ist.
>
> An was denkst du hier genau? So schwerwiegende Probleme, dass ein
> Einsatz nicht sinnvoll möglich wäre, sind mir noch nicht untergekommen.

Ja, noch nocht ;o)  ZB solche Kleinigkeiten, daß der Compiler nicht 
mehr mit LTO zu generieren ist :-/

> Johann L. schrieb:
>> Und ein einziges Commit wie 182084 macht per Handstreich 100 andere
>> Optimierungen im avr-gcc zunichte oder hebelt sie aus, nur weil es auf
>> einem ARM besseren Code gibt:
>
> Das ist natürlich sehr ärgerlich. Vielleicht wäre es besser, so wie
> Microchip vorzugehen. Die verwalten ihren C30 selbst und übernehmen nur
> die Änderungen bzw. synchronisieren nur soweit, wie es nicht zum
> Nachteil ist.

Atmel macht das auch so mit avr32-gcc und avr-gcc. Während erster nicht 
im offiziellen GCC zu finden ist, wird für zweiten ein Atmel-eigener 
Fork unterhalten, der dann zB im AStudio verbacken wird. Auf diese Art 
ist es natürlich viel einfacher, Sachen einzubauen und nicht durch 
Reviews zu müssen oder am GCC-Entwicklungszyklus zu hängen. Und die 
Rechtsabteilung bekommt keine cholerischen Anfälle und die 
Buchungsstelle keinen Herzinfarkt.

Auf die Dauer ist der Aufwand, sich vom offiziellen, großen Bruder 
abzukoppeln, aber nicht zu unterschätzen. Es geht solange einigermassen 
gut, wie es im offiziellen Zweig keine größeren Änderungen gibt und die 
privaten Änderungen problemlos zu mergen sind.

> In der Tat finde ich die Art des "Bugfixings" bei GCC sehr befremdlich.
> Schaut man sich Änderungen, die aus Korrekturen durch Bugmeldungen
> resultieren, genauer an, so fällt auf, dass oftmals nur bei irgendeiner
> "if"-Anweisung in einer Datei mit mehren tausend Zeilen Code zwei oder
> drei weitere Bedingungen angehängt werden. Man kann doch dabei nie im
> Leben alle Eventualitäten abschätzen. Da müssten ja schon Halbgötter am
> Werk sein...

Zum Großteil arbeiten die Entwickler, die dort zugange sind, schon 
einige Zeit am Compiler; rund 10 Jahre und teilweise noch aus der Zeit 
von EGCS. Die kennen sich schon sehr gut damit aus und wissen, was sie 
manchen.

> Johann L. schrieb:
>> Solche Aktionen sind einfach nur frustrierend, und an allen Ecken und
>> quer durch die Foren kann man dann lesen, daß avr-gcc "hier ein
>> unnötiges Byte zu viel" und "dort eine missed Optimization, die jeder
>> Blinke mit Krückstock binnen 2 Sekunden lösen könnte" und warum "avr-gcc
>> so doof ist, dies und jenes nicht zu sehen, was jedem Noob direkt ins
>> Auge springt".
>
> Ich schätze mal, dass das Problem keine "Missed Optimization" ist.

Doch, so weit ich das sehe, ja. Für ARM ist es eben günstiger was anders 
zu machen weil der eben andere Instruktionen hat.

> Man darf nicht vergessen, dass GCC den Anspruch erhebt für kommerzielle
> Anwendung geeignet zu sein. Du hast selbst geschrieben wer dort alles
> mitmischt. Wenn man so ein Tool professionell einsetzt, dann hat man
> vermutlich einfach eine andere Haltung dem gegenüber als wenn man "bloß"
> irgendein Hobbyprojekt einsetzt!

Es hängt auch mit der Wertigkeit von AVR im Vergleich zu ARM oder 
anderen Architekturen zusammen. Es gibt zB einfach zu wenig Support 
für avr-gcc als daß er aufgewertet werden könnte.

> Johann L. schrieb:
>> Leute wie ich, die sich in ihrer Freizeit damit rumbalgen, sind die
>> absolute Ausnahmen.
>
> Aber die machen doch so Projekt sympathisch, meinst du nicht auch?
>
> Ich fände es sehr schade, wenn du damit aufhören würdest. Zumal neben
> dir zurzeit sonst nicht sehr viel passiert.

Soweit ich weiß, ist Sean dabei, den Fixed-Support auf 4.7 zu portieren.

> Jörg Wunsch schrieb:
>> Das sind Leute, die schlicht keinen Schimmer haben, wie ein Compiler
>> arbeitet, bitte ignorier' sie.
>
> Findest du nicht, dass du hier etwas zu viel pauschalierst? Nur weil
> nicht jeder am dem Teil mit herumschraubt, musst du nicht generell
> sämtliche Kompetenz den Leuten absprechen, die möglicherweise Kritik
> üben.

Jörg bezog sich ja nicht allgemen auf "Kompetenz", sondern auf "wie ein 
Compiler" arbeitet; Compiler im allgemeinen und GCC im speziellen.


Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Der eigentliche Übelstand ist doch, daß avr-libc keine libc ist, sondern
>> viele andere Dinge mitbringt, die nicht in eine libc gehören, ...
>
> Das ist aber gängige Praxis.  Welche C-Bibliothek besitzt denn nur
> solche Routinen, die im C-Standard genormt sind?  Mehr oder weniger
> jeder wirft da noch einen Haufen anderer Dinge mit rein, um davon zu
> profitieren, dass sie auf diese Weise `by default' gleich mit sichtbar
> sind.

Solangees Algorithmen wie balancierte Bäume, Hashtables oder FFT wären, 
merkt man ja auch nix davon. Aber wenn die libc schon Ansätze eines HAL 
mitbringt, ...naja.

>> Weiters würde avr-gcc dann von der libc anhängig, was nicht durchgehen
>> wird, da auch newlib funktionieren muss, siehe RTEMS.
>
> Dann doch lieber eine Multilib pro Device?

Denkbar wäre ein Mechanismus wie für PR28718 vorgeschlagen.

>> "Fix von PR51345" bedeutet ein Aufspalten von avr2 und avr25, die bis
>> dato munter Devices mit 8-Bit SP und solche mit 16-Bit SP mischen.
>
> Wobei das aus meiner Sicht ein rein kosmetischer Fix ist (und
> natürlich eine bessere Optimierung auf den kleinen Devices, weil dann
> SPH gar nicht mehr angefasst werden muss), ...

Auf den Kleinen kann SPH doch garnicht angefasst werden, weil es SPH 
garnicht gibt? Momentan wird einfach eine "Reserved Location" gelesen 
und im High-Byte zB des Framepointers steht dann natürlich Müll.

> (__builtin_avr_delay_cycles)
>
>>> Lieber wäre es mir gewesen, wenn für derartige neue Features der
>>> Präprozessor einen Makro zusätzlich bekommt, der das Feature anzeigt.
>>
>> Gibt es, siehe
>> http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html
>
> Danke, das habe ich dann damals entweder übersehen oder aber es war
> zu dem Zeitpunkt noch nicht so implementiert.  Ich werde gern das
> <util/delay.h> dann wieder ändern.

Die Doku bezieht sich auf das, was im FSF ist. Wie es andere 
Branches/Forks handhaben, dazu kann ich nichts sagen.

Ich habe die Makros definiert, weil ich sie zweckmässiger finde als ein 
#ifdef-Salat aus Versionsabfragen auf Anwenderseite. Named Addresses und 
int24 kann ebenso abgefragt werden.

von Roland H. (batchman)


Lesenswert?

Johann L. schrieb:
> Atmel macht das auch so mit avr32-gcc und avr-gcc. Während erster nicht
> im offiziellen GCC zu finden ist, wird für zweiten ein Atmel-eigener
> Fork unterhalten, der dann zB im AStudio verbacken wird.

Ist das der Grund, weshalb in dieser Liste ...

Johann L. schrieb:
> GCC-Entwicker arbeiten für Google, IBM, ARM, CodeSourcery, Red Hat,
> SUSE, AMD, Hewlett-Packard.

... Atmel nicht auftaucht?

Aus welcher Quelle kommt dann z. B. der Debian avr-gcc?
Wenn ich das richtig sehe, "upstream" + FreeBSD patches. Woher kommen 
dann die FreeBSD patches?

Sind die Atmel-Forks öffentlich zugänglich?

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


Lesenswert?

Lars R. schrieb:

> Was meinst du konkret? So überladen ist doch die avr-libc gar nicht. Mir
> fallen spontan die Delay-Sachen sowie die CRC-Berechnung ein.

Der größte Teil dieser Dinge ist ja wirklich komplett in Headerfiles.
Manchmal ist aber sinnvoller, wirklich auch Bibliotheksfunktionen
bereitzustellen, da eine Implementierung im Header zwangsläufig nur
inline erfolgen kann und damit bei mehrmaliger Benutzung zusätzlichen
Code generiert.

> Jörg Wunsch schrieb:
>> Das sind Leute, die schlicht keinen Schimmer haben, wie ein Compiler
>> arbeitet, bitte ignorier' sie.
>
> Findest du nicht, dass du hier etwas zu viel pauschalierst?

Es ging um die "ewigen Nörgler", also um Leute, die zwar keinen
Schimmer haben, wie man einen Compiler aufbaut, die aber bei der
ersten 16-bit-Operation, die sich auch in 8 bits abbilden ließe,
sofort herumtrompeten, dass das doch jeder Trottel merken müsste und
es unglaublich ist, warum GCC derart miesen Code erzeugt.

Johann L. schrieb:
> Und Dinge wie eeprom_* sind doch über Header lösbar?

Hatten wir früher, ist aber einer der Fälle, wo man mit einer
Bibliotheksfunktion wohl schon ab dem zweiten Aufruf Code spart.  Code
sparen ist nunmal das, was die AVR-Anwender typischerweise
priorisieren (bei den CPU-Zyklen haben sie in der Regel genügend
Reserven).

> Ich hab mal überlegt, ob es sinnvoll wäre, RELOCs für SFRs zu haben?

Ich will's nicht als Unsinn abtun, aber es würde bedeuten, große Teile
der Hardware-Kenntnis in den Linker zu verlagern.  Da diese Kenntnis
derzeit nur über ein Headerfile vermittelt wird, sehe ich das als
nicht wirklich trivial an, wie man sowas anders organisieren will.

> Atmel macht das auch so mit avr32-gcc und avr-gcc. Während erster nicht
> im offiziellen GCC zu finden ist, wird für zweiten ein Atmel-eigener
> Fork unterhalten, der dann zB im AStudio verbacken wird.

Der AVR32-Port leidet unter dem gleichen Problem wie der Xmega-Suport:
der blöden Copyright-Abtretung an die FSF.  Die ist zwar inzwischen
durch, aber wenn die eigenen Patches viele Versionen früher gezimmert
worden sind, dann hat man danach ein endloses Katz-und-Maus-Spiel,
wenn man das auf den aktuellen trunk portieren will.

Johann L. schrieb:
> Aber wenn die libc schon Ansätze eines HAL
> mitbringt, ...naja.

Das ist halt das, was man im embedded-Bereich vorrangig braucht.  Wir
konkurrieren hier eben nicht mit der halbherzigen newlib (die für
nichts richtig passt, und für die man für jedes Target noch mächtig
viel Handarbeit ringsum braucht), sondern wir konkurrieren knallhart
mit IAR (der einstmals von Atmel klar favorisiert worden ist) und mit
anderen Compilern wie Codevision, die ebenfalls eine "ready to run"-
Umgebung bieten, oder mit Systemen wie BASCOM.

Ich bin mal so vermessen zu behaupten, dass gerade die Art und Weise,
wie AVR-GCC und avr-libc zusammenspielen (die initiiert worden ist
durch die einstige Personalunion der Hauptakteure Denis Chertykov und
Marek Michalkiewicz) dem GCC zu einer Akzeptanz im Embedded-Bereich
verholfen haben, die er vor reichlich 10 Jahren in dieser Form noch
nicht hatte.  (Die gute Qualität des ARM-Ports tut ein übriges in
dieser Richtung am "oberen Ende" des Embedded-Bereichs.)

>> Dann doch lieber eine Multilib pro Device?
>
> Denkbar wäre ein Mechanismus wie für PR28718 vorgeschlagen.

Muss ich mir mal ansehen.

(PR51345)

> Auf den Kleinen kann SPH doch garnicht angefasst werden, weil es SPH
> garnicht gibt? Momentan wird einfach eine "Reserved Location" gelesen
> und im High-Byte zB des Framepointers steht dann natürlich Müll.

Ist egal, das Register wird auf allen existierenden Implemntierungen
des AVR einfach als 0 gelesen.  Selbst wenn es nicht 0 wäre sondern
wirlich Müll: da die oberen 8 bits der Adresse auf diesen Devices gar
nicht relevant sind, ist es auch egal, was man da reinschreibt.  Es
wird trotzdem die gleiche Speicherstelle angesprochen.  Daher hat
diese Trennung ja auch viele Jahre lang keinen interessiert.  Das
Ganze ist nur eine Performance-/Optimierungsfrage: wenn es SPH gar
nicht gibt, kann man sich natürlich auch beim Anlegen eines
Stackframes oder dergleichen das Herumopern auf den oberen 8 bits der
Adresse sparen.

>> (__builtin_avr_delay_cycles)

> (Makro dafür)

> Die Doku bezieht sich auf das, was im FSF ist. Wie es andere
> Branches/Forks handhaben, dazu kann ich nichts sagen.

Die avr-libc-Implementierung basierte auf einem frühen
Implementierungsvorschlag für delay_cycles, der lange Zeit nur als
WinAVR-Patch herumlungerte, da Anatolyj die ganze Idee nicht wirklich
gut fand.  Da gab es meines Wissens das Makro-Pendant jedoch nicht.

> Ich habe die Makros definiert, weil ich sie zweckmässiger finde als ein
> #ifdef-Salat aus Versionsabfragen auf Anwenderseite. Named Addresses und
> int24 kann ebenso abgefragt werden.

Ja, ich finde das ja auch gut so.

Roland H. schrieb:

> Wenn ich das richtig sehe, "upstream" + FreeBSD patches. Woher kommen
> dann die FreeBSD patches?

Von mir. ;-)  Ich habe die aber (zu) lange nicht mehr angefasst.  Ich
habe mir immer Mühe gegeben, sie mit Erics WinAVR-Patches einigermaßen
zu koordinieren, auch wenn ich nicht alles 1:1 übernommen habe
(beispielsweise habe ich nur einen C/C++-Compiler haben wollen und
daher alle Patches nicht übernommen, die ausschließlich für AVR-Ada
gut waren).

> Sind die Atmel-Forks öffentlich zugänglich?

Als Patches zumindest.  In dieser Hinsicht finde ich es besser als
bei Codesourcery/ARM, bei denen man nur einen einzigen Sourcecode-
Klumpen bekommt, der letztlich aus irgendeiner SVN-Version des GCC
besteht (kein Release) und darauf aufsetzend sackweise eigener
Patches hat, die aber nicht separat ausgewiesen sind.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Roland H. schrieb:
> Johann L. schrieb:
>> Atmel macht das auch so mit avr32-gcc und avr-gcc. Während erster nicht
>> im offiziellen GCC zu finden ist, wird für zweiten ein Atmel-eigener
>> Fork unterhalten, der dann zB im AStudio verbacken wird.
>
> Ist das der Grund, weshalb in dieser Liste ...
>
> Johann L. schrieb:
>> GCC-Entwicker arbeiten für Google, IBM, ARM, CodeSourcery, Red Hat,
>> SUSE, AMD, Hewlett-Packard.
>
> ... Atmel nicht auftaucht?

Dazu könnte ich nur Gerüchte beitragen... Und wie die Befindlichkeiten 
bei Atmel intern sind, da hab ich keinen Einblick.

Generell ist es so, daß GCC-Experten nicht einfach per Stellenanzeige zu 
finden sind und i.d.R. gut versorgt sind, siehe Liste der Arbeitgeber 
oben. Hinzu kommt, daß man sich GCC-Erfahrung nicht einfach mal so 
anlesen kann — so paradox das für eine Open-Source Projekt klingt — und 
als Lone Wolf und ohne GCC-Erfahrung als GCC-Entwickler zB bei einem 
Hardwarehersteller anzuheuern, der gerne GCC-Support für sein Silizium 
hätte, ist fast sicher zum Scheitern verurteilt: Die Lernkurve ist nicht 
gerade flach.

Andererseit ist es durch die Struktur der Tools und mit entsprechender 
Erfahrung möglich, selbst als 1-Mann-Firma eine State-Of-The-Art 
Toolchain für ein neues Silizium zu bieten, siehe 
http://www.embecosm.com

> Sind die Atmel-Forks öffentlich zugänglich?

Für GPL-Software gilt folgendes: Bekommst du von jemand auf legalem Wege 
(Download, geschenkt, gekauft, ...) ein GPL-Programm, so hast du auch 
ein Anrecht darauf (zum Selbstkostenpreis) die Quellen zu bekommen.

Bei WinAVR liegen idR nur die Patches gegen das jeweilige GCC-, 
binutils-, GDB-,... Release bei

Für private Ports gibt es unterschiedliche Geschäftsmodelle:

• Die Tools werden zum Download offen zur Verfügung gestellt;
  typischerweise durch Hardwaranbieter oder beauftragte Drittfirma.
  Beispiel sind AVR32 oder große PICs.

• Die Tools werden von einem Softwarehaus angeboten, das nicht näher
  mit dem Hardwarehersteller verbandelt ist. IdR Kosten die Tools wie
  proprietäre Software auch (zumindest in der Größenordnung), es gibt
  Support, etc.
  Der einzige Unterschied zu proprietären Tools ist, daß der
  Anwender/Kunde Zugang zu den Quellen hat — nicht ganz unwichtig, wenn
  man sich anschaut, wie kurzlebig manch Firmen heutzutage sind oder wie
  schnell sich die Befindlichkeiten bezüglich Hardware verschieben,
  wenn Software-Firmen über den Börsentisch wandern.


Jörg Wunsch schrieb:
> Lars R. schrieb:
>
>> Was meinst du konkret? So überladen ist doch die avr-libc gar nicht. Mir
>> fallen spontan die Delay-Sachen sowie die CRC-Berechnung ein.
>
> Der größte Teil dieser Dinge ist ja wirklich komplett in Headerfiles.
> Manchmal ist aber sinnvoller, wirklich auch Bibliotheksfunktionen
> bereitzustellen, da eine Implementierung im Header zwangsläufig nur
> inline erfolgen kann und damit bei mehrmaliger Benutzung zusätzlichen
> Code generiert.

Das ist mir mal ganz gründlich aufgestoßen, als ich eeprom-Routinen aus 
der avr-libc verwenden wollte: Die Applikation war super schlank und 
effizient aber plötzlich ziemlich aufgebläht; nämlich mehrere 100 Bytes 
größer!

Ziemlich schnell fand ich dann den Übeltäter: Diese Funktionen (wie die 
gesamte avr-libc) sind mit -mcall-prologues erzeugt, und dadurch hat's 
mir über das eeprom-Zeugs das Prolog-Epilog-Geraffel reingezogen.

Ergo: avr-libc-Zeugs wieder raus und Handarbeit her.

> Johann L. schrieb:
>> Und Dinge wie eeprom_* sind doch über Header lösbar?
>
> Hatten wir früher, ist aber einer der Fälle, wo man mit einer
> Bibliotheksfunktion wohl schon ab dem zweiten Aufruf Code spart.  Code
> sparen ist nunmal das, was die AVR-Anwender typischerweise
> priorisieren (bei den CPU-Zyklen haben sie in der Regel genügend
> Reserven).

Zum Code-Sparen siehe oben.

>> Atmel macht das auch so mit avr32-gcc und avr-gcc. Während erster nicht
>> im offiziellen GCC zu finden ist, wird für zweiten ein Atmel-eigener
>> Fork unterhalten, der dann zB im AStudio verbacken wird.
>
> Der AVR32-Port leidet unter dem gleichen Problem wie der Xmega-Suport:
> der blöden Copyright-Abtretung an die FSF.  Die ist zwar inzwischen
> durch, aber wenn die eigenen Patches viele Versionen früher gezimmert
> worden sind, dann hat man danach ein endloses Katz-und-Maus-Spiel,
> wenn man das auf den aktuellen trunk portieren will.

Nicht, wenn man zeitnah den offiziellen GCC beimischt.

Aber zugegeben: von 4.6 -> 4.7 gibt es einige Änderungen, die ein Merge 
nicht gerade einfacher machen; und ich hab noch weitere Aufräumarbeiten 
in der Planung.

Wo ewig nicht gewischelt wurde, da wird der Frühjahrsputz eben um so 
ausgewachsener aus ;-)

> Johann L. schrieb:
>> Aber wenn die libc schon Ansätze eines HAL
>> mitbringt, ...naja.
>
> Das ist halt das, was man im embedded-Bereich vorrangig braucht.  Wir
> konkurrieren hier eben nicht mit der halbherzigen newlib (die für
> nichts richtig passt, und für die man für jedes Target noch mächtig
> viel Handarbeit ringsum braucht), sondern wir konkurrieren knallhart
> mit IAR (der einstmals von Atmel klar favorisiert worden ist) und mit
> anderen Compilern wie Codevision, die ebenfalls eine "ready to run"-
> Umgebung bieten, oder mit Systemen wie BASCOM.

Ich dachte Atmels hat als One-Click-From-Idea-To-Product-Flagschiff 
AStudio und nicht die halbherzig angepackten AVR-Tools? Wenn es für eine 
500MB-oder-noch größer-Software ein Problem ist, ein Schalterchen 
dazuzubasteln... naja.

> Die gute Qualität des ARM-Ports tut ein übriges in
> dieser Richtung am "oberen Ende" des Embedded-Bereichs.

Ohne den GCC-Entwicklern bei Atmel zu nahe treten zu wollen: Zu der 
Expertisem die, etwa CodeSoucery mirbringt, liegen Welten.

>> Denkbar wäre ein Mechanismus wie für PR28718 vorgeschlagen.
>
> Muss ich mir mal ansehen.

Per configure wie ein bestimmtes make-Schnippel getriggert, zB ein 
t-avrlibc in ./gcc/config/avr und/oder in ./libgcc/config/avr

Allerdings muss man dann beim configure das richtige "Sesam öffe dich!" 
angeben.

> (PR51345)
>
>> Auf den Kleinen kann SPH doch garnicht angefasst werden, weil es SPH
>> garnicht gibt? Momentan wird einfach eine "Reserved Location" gelesen
>> und im High-Byte zB des Framepointers steht dann natürlich Müll.
>
> Ist egal, das Register wird auf allen existierenden Implemntierungen
> des AVR einfach als 0 gelesen.  Selbst wenn es nicht 0 wäre sondern
> wirlich Müll: da die oberen 8 bits der Adresse auf diesen Devices gar
> nicht relevant sind, ist es auch egal, was man da reinschreibt.

D.h. PR51002 ist keine "wrong-code" sondern lediglich eine 
"missed-optimization"?

> Es wird trotzdem die gleiche Speicherstelle angesprochen.
> Daher hat diese Trennung ja auch viele Jahre lang keinen interessiert.
> Das Ganze ist nur eine Performance-/Optimierungsfrage: wenn es
> SPH gar nicht gibt, kann man sich natürlich auch beim Anlegen eines
> Stackframes oder dergleichen das Herumopern auf den oberen 8 bits der
> Adresse sparen.

Das wird nun auch so gemacht — bis auf den Code in der libgcc.
Findest du neue multilibs dafür Overkill?

>>> (__builtin_avr_delay_cycles)
>
>> (Makro dafür)
>
>> Die Doku bezieht sich auf das, was im FSF ist. Wie es andere
>> Branches/Forks handhaben, dazu kann ich nichts sagen.
>
> Die avr-libc-Implementierung basierte auf einem frühen
> Implementierungsvorschlag für delay_cycles, der lange Zeit nur als
> WinAVR-Patch herumlungerte, da Anatolyj die ganze Idee nicht wirklich
> gut fand.  Da gab es meines Wissens das Makro-Pendant jedoch nicht.

Das, was ich eingebaut habe, ist i.W. von Anatoly & Eric. Allerdings hab 
ich auch meinen Senf dazugegeben wie u.a. diese Makros.

von Lars R. (larsr)


Lesenswert?

Ich habe mir vorgestern einen aktuellen Compiler erstellt. Damit habe 
ich die verschiedenen Neuerungen getestet. Mit dem Ersatz für das 
PROGMEM habe ich aber noch irgendwie Probleme.

Bisher schrieb ich für einen String im Flash-ROM:
1
static const char str[] PROGMEM = "...";

Eine Tabelle im Flash-ROM auf Strings im Flash-ROM schrieb ich so:
1
static const PGM_P strings[] PROGMEM = { str1, str2 ... };

Ein Zeiger auf so eine Tabelle so:
1
PGM_P *strptr;

Das hatte bisher funktioniert.

Nun habe ich es mit versucht mit "__pgm" zu schreiben. Doch das 
funktioniert nicht. Der Compiler gibt zwar weder Fehler noch Warnung 
aus, aber das Array ist nicht im Flash-ROM. Da dennoch versucht wird 
mittels LPM auf die Daten zuzugreifen werden falsche Daten gelesen.

Ein einzelner String funktioniert jedoch:
1
static const __pgm char str[] = "...";

In der Folge habe ich verschiedene Varianten ausprobiert. Auch ohne 
Erfolg.

Wie muss es nun richtig heißen, wenn man ein Array im Flash-ROM haben 
will, welches auf andere Zeiger im Flash-ROM zeigt? Und wie muss ein 
Zeiger im RAM heißen, der auf genau so eine Tabelle zeigt?

In der Praxis kommt so etwas beispielsweise vor, wenn man Strings in 
mehreren Sprachen im Flash-ROM hat, welche durch Arrays nach außen hin 
zusammengefasst sind, so dass durch einheitliche Indizes darauf 
zugegriffen werden kann und je nach Konfiguration ein Zeiger entweder 
mit der Adresse das Arrays der einen oder der anderen Sprache geladen 
wird.

Ich hatte das eigentlich zunächst so verstanden, dass "__pgm" wohl genau 
so einfach in der Anwendung wäre wie "code" bei den Compilern für 
8051er. Also dass der Anwender nicht mehr Library-Funktionen nutzen 
muss, sondern der Compiler automatisch die notwendigen LPM-Instruktionen 
bei Verwendung dieses Konstrukts korrekt erzeugt. Ist dem nicht so?

von Stefan E. (sternst)


Lesenswert?

Lars R. schrieb:
> Wie muss es nun richtig heißen, wenn man ein Array im Flash-ROM haben
> will, welches auf andere Zeiger im Flash-ROM zeigt?
1
const __pgm char * const __pgm array[] = ...

Lars R. schrieb:
> Und wie muss ein
> Zeiger im RAM heißen, der auf genau so eine Tabelle zeigt?
1
const __pgm char * const __pgm * ptr = array;

Ungetestet, aber Johann wird schon Bescheid sagen, wenn es falsch ist. 
;-)

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


Lesenswert?

Johann L. schrieb:

> Ziemlich schnell fand ich dann den Übeltäter: Diese Funktionen (wie die
> gesamte avr-libc) sind mit -mcall-prologues erzeugt, und dadurch hat's
> mir über das eeprom-Zeugs das Prolog-Epilog-Geraffel reingezogen.

Und wo ist dein Bugreport dafür?

Instinktiv habe ich eigentlich was dagegen, standardmäßig in den
CFLAGS irgendwelche -m- oder -f-Optionen jenseits von -mmcu
einzubauen.  Sowas mag als Einzelfalloptimierung ja sinnvoll sein,
aber wenn man es pauschal irgendwo angibt und damit immer eine
Optimierung erreichen kann, dann sollte es wohl eher von -Os selbst
mit aktiviert werden.  (Offenbar ist es ja aber keineswegs immer eine
Optimierung.)

Bislang hatte ich aber außer diesem Bauchgefühl ("sowas macht man
nicht") nichts konkretes.  Schreib' also bitte einen avr-libc-
Bugreport, und ich werde mich dafür einsetzen, dass wir das aus der
Bibliothek rauswerfen.

>> ..., aber wenn die eigenen Patches viele Versionen früher gezimmert
>> worden sind, dann hat man danach ein endloses Katz-und-Maus-Spiel,
>> wenn man das auf den aktuellen trunk portieren will.
>
> Nicht, wenn man zeitnah den offiziellen GCC beimischt.

Das geht oft aus anderen Gründen nicht so.  Insbesondere wurde in der
Vergangenheit von Version zu Version oft für den AVR schlechterer Code
erzeugt (bedingt durch Änderungen im generischen Teil), sodass man
seine eigenen Quellen nicht gleich auf trunk hochzieht (und beides
parallel zu pflegen kostet halt wieder man power).

> Ich dachte Atmels hat als One-Click-From-Idea-To-Product-Flagschiff
> AStudio und nicht die halbherzig angepackten AVR-Tools? Wenn es für eine
> 500MB-oder-noch größer-Software ein Problem ist, ein Schalterchen
> dazuzubasteln... naja.

Wo habe ich von AVR Studio geschrieben?

Das kann ich schon mangels eines kompatiblen Betriebssystems für mich
gar nicht in Betracht ziehen.

Problematisch mit solchen zusätzlichen Schalterchen ist einfach, dass
das "Produkt" (AVR-Binutils + AVR-GCC + avr-libc) nun schon seit über
10 Jahren am "Markt" ist, und dass es recht schwer ist, da einfach
Änderungen reinzubringen, die inkompatibel zum Vorgänger sind.  Been
there, done that.  Wahrscheinlich hast du noch nichts mit AVRs
gemacht, als wir seinerzeit die nichtsnutzigen sbi()- und cbi()-Makros
(nach mehrjähriger Phase, in der sie "deprecated" waren) aus der
Bibliothek rausgeworfen haben (die weiter nichts waren als als Makro
versteckte |= bzw. &=~ Operatoren).  Es hat uns endlose Diskussionen
im Nachgang gekostet, in denen wir uns immer wieder rechtfertigen
mussten, warum wir das getan haben.  Auch, wenn die
Kommandozeilenoption aus deiner Sicht noch so klein aussieht, eins
habe ich aus der damaligen Aktion gelernt: jede noch so kleine
inkompatible Änderung zieht grenzenlosen Supportaufwand nach sich.
Daher würde ich das Trennen von libc.a und lib${mcu}.a nur dann
mitgehen wollen, wenn zeitgleich das GCC-Frontend beide mit in die
Kommandozeile aufnimmt.  Wenn du dafür (wegen rtems und newlib und
dergleichen) keine Chance siehst, dann finde ich lieber einen Weg für
eine Makefile-Mimik, 50000 einzelne Bibliotheken zu bauen, oder
irgendwas anderes.

>> Die gute Qualität des ARM-Ports tut ein übriges in
>> dieser Richtung am "oberen Ende" des Embedded-Bereichs.
>
> Ohne den GCC-Entwicklern bei Atmel zu nahe treten zu wollen: Zu der
> Expertisem die, etwa CodeSoucery mirbringt, liegen Welten.

Du hast das jetzt komplett aus dem Zusammenhang gerissen: es geht hier
überhaupt nicht um den GCC-Anteil dabei, sondern um das "Rundrum".
Darum, dass man beim ARM eben alles "zu Fuß" machen muss.
Codesourcery popelt dass dann halt nutzerfreundlich über eine
(kostenpflichtige) IDE zurecht, aber wer das nicht benutzen will oder
kann, muss sich durch all das einzeln erstmal durchwühlen.  Ist eine
ziemlich hohe Einstiegshürde.  Ich bin nun gewiss kein Anfänger in der
Benutzung der GNU-Tools und in der Controller-Programmierung, aber ich
habe eine Weile gebraucht, bis ich verstanden habe, was man da alles
wo zusammennageln muss, um endlich das erste Mal zur blinkenden LED zu
kommen.  (Was natürlich bei ARM erschwerend dazu kommt ist, dass man
die herstellerabhängigen Teile von den herstellerunabhängigen trennen
muss.  Manchmal ist single-vendor wie bei AVR auch ein Vorteil, wenn
man dafür eine Softwarelösung anbieten will.)

Nein, die AVR-Toolchain möchte ich so haben wie eine beliebige
C-Umgebung auf dem PC: für das erste "Hello world" soll es genügen,
dass man

avr-gcc -mmcu=meincontroller -Os -o hw.elf hw.c

schreibt.  Auf deinem PC musst du dann auch nicht noch eine Bibliothek
für Posix mit angeben, weil man das ja für die Ausgabe auf das
Terminal braucht...  ("Hello world" ist dabei natürlich bildlich
gemeint und impliziert auf einem Controller eher eine blinkende LED
denn irgendeine Textausgabe.)

> D.h. PR51002 ist keine "wrong-code" sondern lediglich eine
> "missed-optimization"?

Es ist "politically wrong code", würde ich es mal nennen.  Der
wesentliche negative Effekt für den Nutzer dabei ist eine missed
optimization, denn das Lesen und Schreiben der reservierten IO
location 0x3E hat auf keinem existierenden AVR irgendeine negative
Auswirkung.  Allerdings verstößt man natürlich gegen das Gebot,
reservierte Register nicht zu lesen oder zu schreiben, daher ist es
nicht "politically correct", und auch aus dieser Sicht ist die
Trennung in zwei Multilibs ganz sicher der vernünftige Weg.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Lars R. schrieb:
> Johann L. schrieb:
>> Hast du Beispiele?
>
> Ja, natürlich. Der C-Code:
1
// Ruft den aufsummierten Wert ab.
2
signed char GetEncoderDelta(void)
3
{
4
    unsigned char sreg;
5
    sreg = SREG;
6
    cli();
7
    signed char n = m_CurrentDelta;
8
    m_CurrentDelta &= 0x01;
9
    SREG = sreg;
10
    n >>= 1;
11
    return n;
12
}
> Erzeugt dieses: ...
>
> Wieso wird hier der Z-Pointer zurechtgebaut, wenn es auch ein LDS und
> ein MOV täte. Zum Speichern wird dann noch nicht einmal der Pointer

Hab's mal angetestet. "Mein" 4.7 macht zuzüglich
1
extern signed char volatile m_CurrentDelta;
2
3
#define SREG (*((unsigned char volatile*) 0x5f))
4
#define cli __builtin_avr_cli
daraus:
1
GetEncoderDelta:
2
  in r18,__SREG__   ;  6  movqi_insn/4  [length = 1]
3
  cli   ;  7  disable_interrupt  [length = 1]
4
  lds r24,m_CurrentDelta   ;  8  movqi_insn/4  [length = 2]
5
  lds r25,m_CurrentDelta   ;  9  movqi_insn/4  [length = 2]
6
  andi r25,lo8(1)   ;  10  andqi3/2  [length = 1]
7
  sts m_CurrentDelta,r25   ;  11  movqi_insn/3  [length = 2]
8
  out __SREG__,r18   ;  13  movqi_insn/3  [length = 1]
9
  asr r24   ;  19  ashrqi3/3  [length = 1]
10
  ret   ;  29  return  [length = 1]


Jörg Wunsch schrieb:
> Johann L. schrieb:
>
>> Ziemlich schnell fand ich dann den Übeltäter: Diese Funktionen (wie die
>> gesamte avr-libc) sind mit -mcall-prologues erzeugt, und dadurch hat's
>> mir über das eeprom-Zeugs das Prolog-Epilog-Geraffel reingezogen.
>
> Und wo ist dein Bugreport dafür?

Ich ging davon aus, daß es ein "Feature" ist nach dem Motto 
-mcall-prologues=gut -mno-call-prologues=böse.

> Instinktiv habe ich eigentlich was dagegen, standardmäßig in den
> CFLAGS irgendwelche -m- oder -f-Optionen jenseits von -mmcu
> einzubauen.  Sowas mag als Einzelfalloptimierung ja sinnvoll sein,

So?

eeprom.o: CFLAGS += -mno-call-prologues

> Bislang hatte ich aber außer diesem Bauchgefühl ("sowas macht man
> nicht") nichts konkretes.  Schreib' also bitte einen avr-libc-
> Bugreport, und ich werde mich dafür einsetzen, dass wir das aus der
> Bibliothek rauswerfen.

AFAIK war es die Annahme über eine Optimierung, die nicht immer 
ausgeführt wurde, also abhängig von der Compiler-Version/Schalter.

Genau nachverfolgt hab ich es nicht; die avr-libc benutzt einen einen 
indirekten Funktionsaufruf und ging davon aus, das gcc den wegoptimiert, 
was aber nicht geschah.

>>> ..., aber wenn die eigenen Patches viele Versionen früher gezimmert
>>> worden sind, dann hat man danach ein endloses Katz-und-Maus-Spiel,
>>> wenn man das auf den aktuellen trunk portieren will.
>>
>> Nicht, wenn man zeitnah den offiziellen GCC beimischt.
>
> Das geht oft aus anderen Gründen nicht so.  Insbesondere wurde in der
> Vergangenheit von Version zu Version oft für den AVR schlechterer Code
> erzeugt (bedingt durch Änderungen im generischen Teil), sodass man
> seine eigenen Quellen nicht gleich auf trunk hochzieht (und beides
> parallel zu pflegen kostet halt wieder man power).

Wenn es Verschlechterungen bei neueren Versionen aufgrund von Änderungen 
im generischen Teil gibt, dann hat man mit nem privaten Port schon 
überhaupt keine Basis für einen GCC-Bugreport, es sei denn, es lässt 
sich im offiziellen GCC nachweisen.

Bugreports werden aber auch nur dann geschehen, wenn Leute das wirklich 
einsetzen oder zumindest Atmel-Entwickler, denen das unangenehm 
auffällt, Reports machen anstatt sich im Mausloch einer uralt-Version zu 
verkriechen.

Mal dahingestellt wie freudig solche PRs bearbeitet werden: Ohne PR 
werden sie garantiert nicht behoben.

> Problematisch mit solchen zusätzlichen Schalterchen ist einfach, dass
> das "Produkt" (AVR-Binutils + AVR-GCC + avr-libc) nun schon seit über
> 10 Jahren am "Markt" ist, und dass es recht schwer ist, da einfach
> Änderungen reinzubringen, die inkompatibel zum Vorgänger sind.  Been
> there, done that.  Wahrscheinlich hast du noch nichts mit AVRs
> gemacht, als wir seinerzeit die nichtsnutzigen sbi()- und cbi()-Makros
> (nach mehrjähriger Phase, in der sie "deprecated" waren) aus der
> Bibliothek rausgeworfen haben (die weiter nichts waren als als Makro
> versteckte |= bzw. &=~ Operatoren).

Ich dachte das waren Makros, die auf Inline-Assembler abbildeten?

Du wirst lachen: ich hatte überlegt, ob CBI/SBI als built-in sinnvoll 
sind. Grund: Viele Quellen schreiben

PORTX |= 1;

und gehen davon aus, daß das als SBI übersetzt wird. Insbesondere wird 
die Annahme gemacht, daß der erzeugte Code atomar ist, was allerdings in 
keinster Weise durch die Sprache sichergestellt ist.

Die einzigen Möglichkeiten, das robust zu haben, sind:
* built-in
* inline assembler
* atomic um die Instruktion

Schau einfach auf den Code, der mit -O0 erzeugt wird. Hier ist das 
Verhalten des Codes (atomar/nicht-atomar) von der 
Optimierungs-Verschalterung abhängig — nicht wirklich prickelnd.

> Daher würde ich das Trennen von libc.a und lib${mcu}.a nur dann
> mitgehen wollen, wenn zeitgleich das GCC-Frontend beide mit in die
> Kommandozeile aufnimmt.  Wenn du dafür (wegen rtems und newlib und
> dergleichen) keine Chance siehst, ...

Wie gesagt, es ginge per "Sesam öffne dich!":

configure target_configargs="with_avrlibc=yes" ...

Wenn allerdings jemand unbedaftes eine Distribution macht, ohne die 
magischen Worte gesprochen zu haben, wird sie nicht funktionieren wie 
angedacht.

Alternative wäre, das selbst per Patch zu machen und die specs dahin zu 
trimmen, wo du sie haben willst. Siehe zB avr.h:LIB_SPEC.

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


Lesenswert?

Johann L. schrieb:

sbi/cbi

>>  (die weiter nichts waren als als Makro
>> versteckte |= bzw. &=~ Operatoren).
>
> Ich dachte das waren Makros, die auf Inline-Assembler abbildeten?

Das waren sie vor mehr als 10 Jahren, als der Compiler das noch nicht
selbst optimieren konnte.  Dann hat jemand (Marek, Denis oder beide)
das dem GCC beigebogen, und die Makros wurden durch generische
Bitmanipulationen ersetzt und gleichzeitig als "deprecated" markiert.

Dass sie damit gar nicht zwangsläufig als CBI oder SBI umgesetzt
worden sind (und mithin den Nutzer in die Irre führen), war einer der
Gründe, warum wir sie dann endlich nach Jahren loswerden wollten.
Außerdem ist natürlich jeder Entwickler gehalten, Standard-C zu
schreiben soweit möglich, und Bitmanipulationen haben in Standard-C
zwar eine auf den ersten Blick ungewöhnliche Syntax, sind aber
komplett vom Sprachstandard abgedeckt.  Wenn man sich an die Syntax
der Bitoperatoren erstmal gewöhnt hat, hat man dann später weniger
Probleme beim Umstieg auf einen anderen Controller.

> Wenn allerdings jemand unbedaftes eine Distribution macht, ohne die
> magischen Worte gesprochen zu haben, wird sie nicht funktionieren wie
> angedacht.

OK, ich glaube, damit kann man leben.  Es gibt nicht so viele Leute,
die eigene binary distributions bauen.  Ansonsten kann man das
zumindest in der avr-libc-Doku mit unterbringen.

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Hab's mal angetestet. "Mein" 4.7 macht zuzüglich [...] daraus:

Du hast Recht! Ich habe etwas ganz merkwürdiges mit dem aktuellen GCC 
festgestellt: Es hängt davon ab, ob diese Variable noch in einem anderen 
Modul verwendet wird oder nicht.

Übergebe ich dem Compiler den Code alleine, wie von dir durchgeführt, 
kommt zweimal LDS.

Ist beim Kompilieren jedoch noch mindestens ein weiteres Modul dabei, 
das ebenfalls zugreift, kommt die Z-Pointer-Geschichte. (Das kann man 
natürlich nur mit LTO testen.)

Scheint also irgendwie kein spezielles AVR-Problem, sondern eine "missed 
optimization" in Verbindung mit LTO zu sein. Keine Ahnung welchen 
Einfluss LTO auf die Kostenrechnung im GCC nimmt. Da dies mit avr-gcc < 
Version 4.7 noch anders war, dürfte eine Änderung im GCC-Middleend die 
Ursache sein.

Da ich mit LTO bei diesem Projekt mehrere hundert Bytes Code spare, 
stören mich die paar Bytes unnötiger Code nicht wirklich. Für einen 
Bugreport lohnt das wohl kaum.

Möglich wäre es, dass GCC diese Funktion nutzt um auch dem darauf 
folgenden Code bereits einen nutzbaren Z-Pointer für den Zugriff auf die 
Variable zu hinterlassen. Fraglich ist halt nur, wieso nicht ST statt 
STS genutzt wird.

Wenn man spitzfindig ist, könnte man ja sagen, dass bei -Os LDS/MOV 
besser als LDS/LDS wäre. Ist aber bestimmt nicht so einfach abzufangen, 
oder?

Könnte man für so etwas eine Peephole-Optimierung schreiben? Also statt
1
LDS r16,0xABC
2
LDS r17,0xABC

pasuchal
1
LDS r16,0xABC
2
MOV r17,r16

schreiben?

Johann L. schrieb:
> Schau einfach auf den Code, der mit -O0 erzeugt wird. Hier ist das
> Verhalten des Codes (atomar/nicht-atomar) von der
> Optimierungs-Verschalterung abhängig — nicht wirklich prickelnd.

Das Verhalten dürfte aber auch nicht einfach zu ändern sein, oder?

Der Programmierer geht (mit seinem Assembler-Wissen) vermutlich davon 
aus, dass PORTX &= ~(1 << xy) und PORTX |= (1 << xy) mit CBI/SBI 
umsetzbar sind. Daran zu denken, dass der Compiler es eventuell anders 
sehen könnte, dürfte den meisten unverständlich sein.

Aus dem Stegreif heraus würde ich für so einen Fall vorschlagen, dass 
der Compiler, sobald er feststellt, dass es sich um I/O-Register handelt 
und die Änderung mit einem Befehl realisierbar wäre, in jeden Falle 
CBI/SBI ausgeben muss und das unabhängig von der 
Optimierungseinstellung. Was ich allerdings nicht weiß ist, ob man das 
im Backend so einfach feststellen könnte.

Johann L. schrieb:
> configure target_configargs="with_avrlibc=yes" ...
>
> Wenn allerdings jemand unbedaftes eine Distribution macht, ohne die
> magischen Worte gesprochen zu haben, wird sie nicht funktionieren wie
> angedacht.

Könnte man, wenn man dies so realisieren würde, nicht einen Hinweis 
irgendwo anbringen, dass diese Einstellung erforderlich ist, wenn man 
avr-libc nutzen will? Oder darf auch innerhalb GCC nicht auf 
Fremdprodukte verwiesen werden?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>
> sbi/cbi
>
>>>  (die weiter nichts waren als als Makro
>>> versteckte |= bzw. &=~ Operatoren).
>>
>> Ich dachte das waren Makros, die auf Inline-Assembler abbildeten?
>
> Das waren sie vor mehr als 10 Jahren, als der Compiler das noch nicht
> selbst optimieren konnte.  Dann hat jemand (Marek, Denis oder beide)
> das dem GCC beigebogen, und die Makros wurden durch generische
> Bitmanipulationen ersetzt und gleichzeitig als "deprecated" markiert.
>
> Dass sie damit gar nicht zwangsläufig als CBI oder SBI umgesetzt
> worden sind (und mithin den Nutzer in die Irre führen), war einer der
> Gründe, warum wir sie dann endlich nach Jahren loswerden wollten.
> Außerdem ist natürlich jeder Entwickler gehalten, Standard-C zu
> schreiben soweit möglich, und Bitmanipulationen haben in Standard-C
> zwar eine auf den ersten Blick ungewöhnliche Syntax, sind aber
> komplett vom Sprachstandard abgedeckt.  Wenn man sich an die Syntax
> der Bitoperatoren erstmal gewöhnt hat, hat man dann später weniger
> Probleme beim Umstieg auf einen anderen Controller.

Wie gesagt: Atomarität lässt sich so nicht garantieren.

>> Wenn allerdings jemand unbedaftes eine Distribution macht, ohne die
>> magischen Worte gesprochen zu haben, wird sie nicht funktionieren wie
>> angedacht.
>
> OK, ich glaube, damit kann man leben.  Es gibt nicht so viele Leute,
> die eigene binary distributions bauen.  Ansonsten kann man das
> zumindest in der avr-libc-Doku mit unterbringen.

Im GCC könnte man ja auch was hinschreiben.
Nur wo? Und wer liest das überhaupt?

Lars R. schrieb:
> Johann L. schrieb:
>> Hab's mal angetestet. "Mein" 4.7 macht zuzüglich [...] daraus:
>
> Du hast Recht! Ich habe etwas ganz merkwürdiges mit dem aktuellen GCC
> festgestellt: Es hängt davon ab, ob diese Variable noch in einem anderen
> Modul verwendet wird oder nicht.
>
> Übergebe ich dem Compiler den Code alleine, wie von dir durchgeführt,
> kommt zweimal LDS.
>
> Ist beim Kompilieren jedoch noch mindestens ein weiteres Modul dabei,
> das ebenfalls zugreift, kommt die Z-Pointer-Geschichte. (Das kann man
> natürlich nur mit LTO testen.)

Müsste dann doch auch so sein, wenn's mehrere Funktionen in einem Modul 
hat?

> Scheint also irgendwie kein spezielles AVR-Problem, sondern eine "missed
> optimization" in Verbindung mit LTO zu sein. Keine Ahnung welchen
> Einfluss LTO auf die Kostenrechnung im GCC nimmt. Da dies mit avr-gcc <
> Version 4.7 noch anders war, dürfte eine Änderung im GCC-Middleend die
> Ursache sein.

Wie ober erklärt: Indirekte Zugriffe sind auf praktisch allen 
Architekturen günstiger oder zumindest gleichgut wie direkte. Die 
(akkumulierten) Kosten kennt man aber auf Tree-Ebene nicht wirklich.

> Da ich mit LTO bei diesem Projekt mehrere hundert Bytes Code spare,
> stören mich die paar Bytes unnötiger Code nicht wirklich. Für einen
> Bugreport lohnt das wohl kaum.

Bugreport lohnen immer. Wie oben gesagt: was nicht berichtet wird, wird 
auch nicht behoben.

> Wenn man spitzfindig ist, könnte man ja sagen, dass bei -Os LDS/MOV
> besser als LDS/LDS wäre. Ist aber bestimmt nicht so einfach abzufangen,
> oder?
>
> Könnte man für so etwas eine Peephole-Optimierung schreiben? Also statt
>
1
LDS r16,0xABC
2
LDS r17,0xABC
>
> pasuchal
>
1
LDS r16,0xABC
2
MOV r17,r16
>
> schreiben?

Bezieht sich das auf obigen C-Code? Dann mal das Kleingedruckte lesen, 
insbesondere das, das auf "v" beginnt und mit "olatile" endet.

Pauschal geht es also nicht. Es geht nicht, wenn
* MEM_VOLATILE_P gesetzt ist
* Ein Label zwischen den Insns steht (sieht du nicht im lst-File)

> Johann L. schrieb:
>> Schau einfach auf den Code, der mit -O0 erzeugt wird. Hier ist das
>> Verhalten des Codes (atomar/nicht-atomar) von der
>> Optimierungs-Verschalterung abhängig — nicht wirklich prickelnd.
>
> Das Verhalten dürfte aber auch nicht einfach zu ändern sein, oder?

Die Sprache gibt's eben nicht her. Es geschieht in der generischen 
Optimierung.

Selbst ein eigener Address Space __IO würde nicht helfen, es sei denn 
man implementiert einen neuen Targes-Took und fragt nach, ob eine 
Optimierung für einen bestimmten Space erlaubt ist oder nicht. Viel 
Spaß...

> Der Programmierer geht (mit seinem Assembler-Wissen) vermutlich davon
> aus, dass PORTX &= ~(1 << xy) und PORTX |= (1 << xy) mit CBI/SBI
> umsetzbar sind. Daran zu denken, dass der Compiler es eventuell anders
> sehen könnte, dürfte den meisten unverständlich sein.
>
> Aus dem Stegreif heraus würde ich für so einen Fall vorschlagen, dass
> der Compiler, sobald er feststellt, dass es sich um I/O-Register handelt
> und die Änderung mit einem Befehl realisierbar wäre, in jeden Falle
> CBI/SBI ausgeben muss und das unabhängig von der
> Optimierungseinstellung. Was ich allerdings nicht weiß ist, ob man das
> im Backend so einfach feststellen könnte.

Nein. Um das im Backend zu lösen brauchst du mindestens einen eigenen 
Propagation-Pass o.ä.

> Johann L. schrieb:
>> configure target_configargs="with_avrlibc=yes" ...
>>
>> Wenn allerdings jemand unbedaftes eine Distribution macht, ohne die
>> magischen Worte gesprochen zu haben, wird sie nicht funktionieren wie
>> angedacht.
>
> Könnte man, wenn man dies so realisieren würde, nicht einen Hinweis
> irgendwo anbringen, dass diese Einstellung erforderlich ist, wenn man
> avr-libc nutzen will?

Muss das *irgendwo* nur noch gefunden, gelesen, verstanden und richtig 
angewandt werden.

@Jörg: Wie sieht da denn die Synchronisation gcc <-> libc aus?

Am praktikabelsten würdest doch du den Patch machen und integrieren?

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


Lesenswert?

Johann L. schrieb:

>> sbi/cbi

> Wie gesagt: Atomarität lässt sich so nicht garantieren.

Ja.  Wer nicht optimiert, hat sowieso ganz andere Probleme als
Atomarität. ;-)

(Doku)

> Im GCC könnte man ja auch was hinschreiben.
> Nur wo? Und wer liest das überhaupt?

Ich glaube, dass die avr-libc-Doku durchaus gelesen wird, daher auch
mein Vorschlag.  Da gibt's ohnehin auch schon einen Abschnitt
"Building the GNU tools" (oder so).

> @Jörg: Wie sieht da denn die Synchronisation gcc <-> libc aus?
>
> Am praktikabelsten würdest doch du den Patch machen und integrieren?

Wahrscheinlich wird das an mir hängen bleiben.  Eric käme natürlich
genauso in Frage (wäre eigentlich sogar prädestiniert, da er direkte
Schreibrechte bei GCC hat ;-).

von Lars R. (larsr)


Lesenswert?

Johann L. schrieb:
> Im GCC könnte man ja auch was hinschreiben.
> Nur wo? Und wer liest das überhaupt?

Der Compiler könnte ja eine Warnung während der Erstellung des avr-gcc 
ausgeben, wenn die Option nicht angegeben wäre. Fraglich wäre halt nur, 
ob dies noch mit der Projektpolitik vereinbar wäre.

Johann L. schrieb:
> Müsste dann doch auch so sein, wenn's mehrere Funktionen in einem Modul
> hat?

Das weiß ich nicht, habe ich nicht ausprobiert. Werde ich aber noch 
nachholen.

Mein Problem mit "__pgm" hat sich gelöst. Scheinbar habe ich es versäumt 
ein richtiges Clean zu machen. Nachdem ich mich nochmals vergewissert 
hatte, dass keine älteren Dateileichen mehr existieren, funktionierte es 
auf Anhieb. Tolle Sache!

Wenn das nun alles richtig im GCC verankert ist, spricht ja eigentlich 
auch nichts mehr dagegen, dies später im produktiven Code einzusetzen, 
oder?

Johann L. schrieb:
> Wie ober erklärt: Indirekte Zugriffe sind auf praktisch allen
> Architekturen günstiger oder zumindest gleichgut wie direkte. Die
> (akkumulierten) Kosten kennt man aber auf Tree-Ebene nicht wirklich.

Fließen die eigentlichen Instruktiongrößen (also in Bytes) eigentlich 
tatsächlich in die Kostenrechnung ein? Oder mutmaßt der Compiler nur, 
dass indirekte günstiger als direkte Adressierung ist usw.?

Johann L. schrieb:
> Bezieht sich das auf obigen C-Code? Dann mal das Kleingedruckte lesen,
> insbesondere das, das auf "v" beginnt und mit "olatile" endet.

Scherzkeks. Der Code ist von mir gepostet, noch weiß ich, was ich 
geschrieben habe. ;-)

Das "volatile" ist natürlich ein Totschlagargument. Aber ich frage mich 
halt, ob dies so strikt geschehen muss. Wenn zweimal (oder mehrmals) 
dieselbe Variable geladen wird, ohne dass dazwischen andere 
Instruktionen, oder Labels, stehen dann wäre doch immer noch dem 
"volatile" Genüge getan, selbst wenn man hier etwas optimiert.

Auf Labels muss man allerdings Rücksicht nehmen. Beispiel:
1
volatile uint8_t varxy;
2
...
3
varxy |= 0x01;
4
uint8_t copy = varxy;
5
while (varxy & 0x01);
6
...

Hier könnte man die beiden aufeinanderfolgenden Ladevorgänge natürlich 
nicht zusammenfassen. Täte man dies, stünde in "copy" der Wert nach dem 
Löschen des Flags und nicht der Wert von unmittelbar nach dem Setzen des 
Flags. Aber intern ist in so einem Falle ja sowieso ein Label in Form 
einer Blockgrenze zwischen den beiden Vorgängen.

Nebenbei: In dem Beispielcode bräuchte es die "volatile"-Behandlung 
überhaupt nicht. Interrupts sind ja schließlich ausgeschaltet. Aber das 
kann der Compiler ja nicht wissen.

Johann L. schrieb:
> Pauschal geht es also nicht. Es geht nicht, wenn
> * MEM_VOLATILE_P gesetzt ist
> * Ein Label zwischen den Insns steht (sieht du nicht im lst-File)

Ja, ich weiß. Hinter den Branch-Instruktionen stehen immer nur Offsets 
(+6/-4 usw.).

Johann L. schrieb:
> Nein. Um das im Backend zu lösen brauchst du mindestens einen eigenen
> Propagation-Pass o.ä.

Dem entnehme ich, dass dies wohl nicht mit einer kleinen Funktion in der 
avr.c getan wäre?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Johann L. schrieb:
>> @Jörg: Wie sieht da denn die Synchronisation gcc <-> libc aus?
>>
>> Am praktikabelsten würdest doch du den Patch machen und integrieren?
>
> Wahrscheinlich wird das an mir hängen bleiben.  Eric käme natürlich
> genauso in Frage (wäre eigentlich sogar prädestiniert, da er direkte
> Schreibrechte bei GCC hat ;-).

SVN Write ist kein Thema. Frag Eric, ob er dich sponsort, und in 1-2 
Tagen hast die Schreibrechte.

Lars R. schrieb:
> Johann L. schrieb:
>> Im GCC könnte man ja auch was hinschreiben.
>> Nur wo? Und wer liest das überhaupt?
>
> Der Compiler könnte ja eine Warnung während der Erstellung des avr-gcc
> ausgeben, wenn die Option nicht angegeben wäre. Fraglich wäre halt nur,
> ob dies noch mit der Projektpolitik vereinbar wäre.

Nö.

> Johann L. schrieb:
>> Müsste dann doch auch so sein, wenn's mehrere Funktionen in einem Modul
>> hat?
>
> Das weiß ich nicht, habe ich nicht ausprobiert. Werde ich aber noch
> nachholen.
>
> Mein Problem mit "__pgm" hat sich gelöst. Scheinbar habe ich es versäumt
> ein richtiges Clean zu machen. Nachdem ich mich nochmals vergewissert
> hatte, dass keine älteren Dateileichen mehr existieren, funktionierte es
> auf Anhieb. Tolle Sache!

Und die anderen? __pgmx,__pgm1,...,__pgm5

> Wenn das nun alles richtig im GCC verankert ist, spricht ja eigentlich
> auch nichts mehr dagegen, dies später im produktiven Code einzusetzen,
> oder?

Testabdeckung ist momentan = 0. Ich erinner mich an einen Fehler beim 
Rumwutzen mit Poiter-Casts, kann es aber nicht mehr reproduzieren :-/

> Johann L. schrieb:
>> Wie ober erklärt: Indirekte Zugriffe sind auf praktisch allen
>> Architekturen günstiger oder zumindest gleichgut wie direkte. Die
>> (akkumulierten) Kosten kennt man aber auf Tree-Ebene nicht wirklich.
>
> Fließen die eigentlichen Instruktiongrößen (also in Bytes) eigentlich
> tatsächlich in die Kostenrechnung ein? Oder mutmaßt der Compiler nur,
> dass indirekte günstiger als direkte Adressierung ist usw.?

"Der Compiler" ist ein weites Feld; was um die 3E6 Lines of Code in 2E5 
Dateien... das must selber rausfinden wo da die Kosten eingehen, 
vielleicht bringt dich -mlog=rtx_costs,address_cost ja auf ne Spur.

> Johann L. schrieb:
>> Bezieht sich das auf obigen C-Code? Dann mal das Kleingedruckte lesen,
>> insbesondere das, das auf "v" beginnt und mit "olatile" endet.
>
> Scherzkeks. Der Code ist von mir gepostet, noch weiß ich, was ich
> geschrieben habe. ;-)
>
> Das "volatile" ist natürlich ein Totschlagargument. Aber ich frage mich
> halt, ob dies so strikt geschehen muss.

volatile ist volatile ist volatile ist...

Sind A und B volatile, dann kann selbst das Lesen (sic!) von A einen 
Einfluss auf B haben!

> Wenn zweimal (oder mehrmals) dieselbe Variable geladen wird,
> ohne dass dazwischen andere Instruktionen, oder Labels,
> stehen dann wäre doch immer noch dem
> "volatile" genüge getan, selbst wenn man hier etwas optimiert.

Nein. s.o.

> Johann L. schrieb:
>> Pauschal geht es also nicht. Es geht nicht, wenn
>> * MEM_VOLATILE_P gesetzt ist
>> * Ein Label zwischen den Insns steht (sieht du nicht im lst-File)
>
> Ja, ich weiß. Hinter den Branch-Instruktionen stehen immer nur Offsets
> (+6/-4 usw.)

Guckst du Compiler-Ausgabe mit -save-temps -dp oder mit -dP -da 
-fdump-tree-all wenn du die Dröhnung brauchst.

> Johann L. schrieb:
>> Nein. Um das im Backend zu lösen brauchst du mindestens einen eigenen
>> Propagation-Pass o.ä.
>
> Dem entnehme ich, dass dies wohl nicht mit einer kleinen Funktion in der
> avr.c getan wäre?

Weder wäre die klein, noch würde sie alle Fälle erfassen (können), noch 
wäre es der richtige Ansatz.

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.