C - Compiler

GNU-gcc  Intel/SCO-icc  SCO-cc

Nachfolgend beschreibe ich kurz meine Erfahrungen mit diesen Compilern,
gebe Empfehlungen und nehme Bewertungen vor.

Enthalten ist auch eine Kompilierung eines aktuellen gcc-Quelltextes
mittels gcc als auch einem anderen Compiler.
(Eine eigene alloca() wird vorgestellt.)

Außerdem gebe ich Auskunft über von mir bevorzugte Konventionen bei der C-Programmierung.

'cc' und 'icc' stammen von der Haupt-CD des Unix-Systems SCO OpenServer 5.0.5 (5.0.4) ,
'gcc' (v2.7.2.3) von der CD  SCO Skunkware 98 .

Empfohlene Optionen auf der Grundlage meiner Erfahrungen:

gcc:

    gcc  -funsigned-char -mcoff -s -O1 \
    -fwritable-strings \
    -fomit-frame-pointer \
    -fcaller-saves \
    -fforce-mem \
    -m386 \
    -malign-loops=0 -malign-jumps=0 \
    -malign-functions=3 \
    -malign-double \
    -Wall \
    -Wwrite-strings \
    -Wpointer-arith -Wcast-align \
    -Wstrict-prototypes \
    -Wmissing-prototypes \
    -Wmissing-declarations \
    -Wno-parentheses \
    *.c
    #-Wno-uninitialized \
    #-Wno-char-subscripts \
    #-ansi -pedantic -W \

icc:

    icc  -Xa -w1 -s -O -tp p5 -fp -nofdiv_check  *.c
    -Dbyte="unsigned char" -fp- -O1 -O- -O0 -tp p6

cc (sco):

    cc  -Xa -J -w3 -s -O1 -Kspace  *.c
    (-Kpentium,noinline,frame,nolu)
Compiler   Kodegröße   Geschwindigkeit   Bemerkungen
  gcc        131000      .85 s           -s -mcoff -O1 -fu-char
gcc 116500 .65 s siehe oben
gcc2.8.1 116800 .71 s siehe oben
icc 146000 .70 s siehe oben
cc 123000 .79 s siehe oben

Der gcc gewinnt letztlich durch die enorme Fülle von angebotenen Optionen und wegen
seiner Fähigkeit, (prozessorunabhängig) viele Instruktionen einzusparen.
Der neuere gcc v2.8.1 macht jedoch etwas langsameren Kode.

Jedenfalls bei meinen Quellkodes sind starke Optimierungen stets schädlich:
der Kode wächst und wird langsamer.
Die Wahrscheinlichkeit von Compiler-Bugs wächst dabei ebenfalls;
gcc hat z.B. bei *umfangreicher* Verwendung von '__attribute__ ((regparm(1)))'
('static' functions) Fehler produziert.

Diese drei Compiler haben jeder für sich eine ganz eigene Art und Weise,
Assembler-Kode aus C-Quellen zu erzeugen, gestalten und anzuordnen.

gcc:

Sofern man eine optimale Zusammenstellung der angebotenen Optionen vornimmt,
gelingt es dem gcc am besten, die Anzahl von Instruktionen zu minimieren.
Das erzielbare Ergebnis betreffend ist gcc der beste dieser Compiler.

Zeichenketten-Konstanten werden voreingestellt in den '.text'-Abschnitt (Kode-Segment)
platziert.  Das mag der Pentium performance-mäßig überhaupt nicht!
Auch Sprung-Tabellen (switch) werden in .text gepackt.
Hier ist gcc anders als die beiden anderen Compiler.

Das beste ist aus meiner Sicht:
- const char *    einzusetzen, wann immer es geht,
- mittels  -Wwrite-strings  zu überprüfen,
- und dann abschließend  -fwritable-strings  zu setzen.
Es sei denn, eine Plattform bietet einen Abschnitt '.rodata' (oder ähnlich) an.

icc:

Dieser Compiler optimiert bei weitem am besten auf prozessor-spezifische Weise.
Es ist erstaunlich, wie schnell die hier größte Exe läuft - trotz der Instruktionsüberzahl.
Dieser Compiler wirkt  kühl, sachlich, konservativ-vorsichtig, souverän, professionell.
Er optimiert überwiegend 'physikalisch und unverzwickt', nicht aggressiv aber gut.
Ich bekomme beim Betrachten des asm-Kodes das Gefühl:  "der kann keine Bugs haben".
Wenn er weitere Optimierungsthemen verpaßt bekäme, könnte er die gcc-Ergebnisse
wohl noch übertreffen.

Auf einem PII mit Schalter -tp p6 erzeugt er etwa gleich schnelle Exe wie gcc,
die Exe-Größe steigt dabei auf etwa 171000 Byte.
Hier ist deutlich eine ganz eigene 'Handschrift' zu erkennen!

Er hat keine Option 'unsigned char', akzeptiert nicht '//kommentar',
und Umlaute und dergleichen in Konstanten  "äöüß©ÿ"   'Ö'  sind für ihn
Syntax-Fehler der schlimmsten Sorte.

Die Gestaltung der Meldungen (error,warning) ist vorbildlich.

cc:

Der Standard-Compiler des Development-Systems wirkt irgendwie halbfertig.
Er kompiliert zwar alles fehlerfrei zu ordentlichen Endergebnissen, erweckt aber
aufgrund nicht weniger Details den Eindruck, als wolle man ihn nicht mehr vollenden.
Beispielsweise optimiert er abschnittweise vorzüglich, jedoch an anderen Stellen
wiederum schlecht mit redundanten Instruktionen.
Möglicherweise hatte man neue Konzepte ausprobiert, die sich aber als nicht idealer Weg,
als ein wenig verkorkst herausgestellt haben - er wirkt jedenfalls nicht homogen.

Beim cc des Systems 5.0.2 hatte ich einen sehr seltenen Bug entdeckt und gemeldet.
Die cc der 5.0.4 und 5.0.5 haben diesen Bug nicht mehr, erzeugen aber um etwa 5% größere Exe.
Wahrscheinlich hat man eine bestimmte Optimierung komplett abgeschaltet.

unsigned char

Ich verwende grundsätzlich und ausnahmslos 'unsigned char'.
Falls der Compiler es anbietet, per Option - die allermeisten Compiler haben eine solche Option.
Eine Compiler-Option hat den Vorteil, daß sie auch in allen <header.h> wirkt, also völlig global.
Ich definiere stets

    #if !defined(byte)
    # define byte char
    #endif

und verwende im Quelltext kein 'char', um gegebenenfalls flexibel reagieren zu können.
Fehlt diese Compiler-Option, gebe ich in der Kommandozeile (Script) an:

    -Dbyte="unsigned char"

Es gibt dann einige Warnungen:  char <--> unsigned char
Bei

    typedef  unsigned char  byte;

gibt es noch mehr Warnungen wegen <header.h>, weil hierdurch 'byte' ein
gänzlich neuer Typ ist, weshalb ich die define-Variante vorziehe.

unsigned char  bietet nur Vorteile, keine Nachteile:

Die Programme werden in der Regel kleiner und schneller - auch indirekt.
Eine Verwendung als array-Index führt niemals zu versehentlichen int-Negativwerten.

Oftmals muß man char-Typen -letztlich- als Index verwenden, wie sollte man sonst
beispielsweise Zeichensätze konvertieren (mappen)!?
Ob ein unbeabsichtigter Negativwert bei der Zuweisung an einen int-Typ passiert,
den man anschließend als (indirekten) Index verwenden will,
oder -direkt- bei  [index]  , macht schließlich keinen Unterschied!

Übrigens:

    signed char sc;
    unsigned u;
    u= sc;
    u= (unsigned)sc;
    u= (unsigned char)sc;
    u= (unsigned)(unsigned char)sc;

bei den ersten beiden Zuweisungen wird das Vorzeichen bei der Aufweitung erhalten !
Dieser Denkfehler war mir auch mal passiert - in grauer Vorzeit.
Bei den letzten beiden Zuweisungen wird vor der Aufweitung die Vorzeichenwirkung entfernt.
(Statt 'unsigned u' hätte man auch 'int i' nehmen können - macht bei dieser Betrachtung keinen Unterschied.)

Der Typ (signed) 'char' macht praktisch keinen Sinn.
'char' ist signed voreingestellt, weil alle Integer-Typen dies einheitlich sind,
und erst durch 'unsigned' das Vorzeichen verlieren.
Wer muß/will denn schon im Zahlenbereich  [-128, 127]  mathematisch rechnen und vergleichen?!
Dann nehme man eben 'int' oder bei Unumgänglichkeit punktuell 'signed char'.

Compiler-Vergleich anhand der Assembler-Ausgabe

Es wurden zwei C-Funktionen zum Testen verwendet, die vom programmierten Ablauf her
vollkommen gleich sind, jedoch eine gänzlich unterschiedliche Syntax verwenden.
Die erste Variante verwendet kompromißlos die KO-Logik-Methode,
die zweite Variante kann man als normal bezeichen, sie sieht manierlicher aus.

Beide Varianten wurden mit den drei Compilern kompiliert.

Erzeugte Anzahl von Instruktionszeilen:

    Compiler   Variante 1   Variante 2
      gcc         37           37
    icc 51 51
    cc 85 54

gcc und icc erzeugen jeweils nahezu vollkommen identischen Kode,
(nur andere Label-Namen, bei gcc ein Instruktionsreihenfolgentausch),
bei icc sind die beiden obj-Dateien vollkommen gleich, bei gcc sind 9 Bytes verschieden,
cc ist hier allerdings außerordentlich unterschiedlich.

C-Test-Funktion (w1.c):

    int Input(int ityp)
    {
    register int c;
    while ( PSnu=0, c= List(ityp), ityp&ITYP_I?++KDOnu:0,
    !O['t'] &&
    (c<EoF||(ityp&ITYP_I)&&
    (c>ADD+8&&(!(G.ityp&ITYP_P)||c!=rETURN)
    ||c==EoF&&(O['I']|O['P'])
    &&(write(2,"Benutze exit" NL,13+NLSZ),
    --KDOnu, 1))
      ) );
    return (c);
    }

C-Test-Funktion (w2.c), logische Funktion genau so wie oben:

    int Input(int ityp)
    {
    register int c;
    while ( PSnu=0, c=List(ityp), 1 ) {
    if (ityp&ITYP_I) ++KDOnu;
    if ( O['t']) break;
    if ( c>=EoF) {
    if ( !(ityp&ITYP_I) ) break;
    if ( c<=ADD+8 || (G.ityp&ITYP_P) && c==rETURN ) {
    if ( c!=EoF || !(O['I']|O['P']) ) break;
    write(2, "Benutze exit" NL, 13+NLSZ);
    --KDOnu;
    }
    }
    }
    return (c);
    }

Die 6 Assembler-Ausgaben:

Siehe letzten Tabellenabschnitt.

gcc 2.8.1 - Kompilierung

Diese neue Version von März'99 habe ich mit 3 verschiedenen Compilern versucht zu kompilieren.
Offensichtlich ist die Verwendung von alloca() innerhalb des gcc-Quelltextes ein ziemliches Problem.
Das ist quasi eine Vorwegnahme von C9X, dem kommenden C-Standard, auf die man beim Quelltext
des gcc besser hätte verzichten sollen...
Der gcc ist problemlos und auf Anhieb eigentlich nur durch sich selbst kompilierbar;
man gewinnt durchaus den Eindruck, daß seine Entwickler dies ziemlich darauf abgestellt haben.

Der 'cc' hat alloca() eingebaut:  -O -Kalloca,...
Jedoch bei der etwa 15-ten Anwendung von alloca() in cccp.c(4515)
hat er gestreikt mit  Internal Compiler Error.
Möglicherweise, weil nur gcc allein mit '-g' und '-O' gleichzeitig klarkommt...

Der 'icc' hat alloca() nicht eingebaut, weshalb alloca.c-->alloca.o verwendet wurde.
Der Compiler 'cc1' hatte jedoch bei manchen Aufgaben mit Signal SIGABRT + core abgebrochen,
möglicherweise, weil der Ersatz alloca.c (mit malloc()+free()) irgendwie nicht richtig funktioniert;
fehlerhaft ist der icc nach meiner Erfahrung jedenfalls ganz und gar nicht...
(siehe nächsten Tabellenabschnitt.)

Mit gcc 2.7.2.3 klappte die Angelegenheit jedenfalls,
trotz der vielen Warnmeldungen (siehe unten).
Der gcc2.8.1 akzeptiert auch einige Optionen, die der 2.7.2.3 nicht annimmt.
Auf die Attribute stdcall und cdecl reagiert aber auch er ebenfalls nicht bzw. falsch.

Erfolgreiche Kompilierung mit 'icc' und selbstentwickelter 'alloca.o'

Das ist ein ziemlich starker Hinweis darauf, daß 'alloca.c' aus der gcc-Quelle nicht richtig funktioniert.

Die untenstehende 'alloca.s' funktioniert mit dem gcc-Quelltext einwandfrei, wenn man dafür sorgt,
daß der verwendete Compiler das 'esp'-Register nur auf klassische Weise verwendet.
Eine solche unschädliche Verwendung des Stackpointers kann durch
'Stackframe=ja' und/oder 'Optimierung=nein' erreicht werden.

Assembler-Datei  alloca.s  (ELF):

            .text
    .globl alloca
    alloca:
    movl 4(%esp), %eax
    movl (%esp), %edx
    movl %eax, %ecx
    addl $3, %eax
    andl $0xfffffffc, %eax
    jz .Jret
    / subl $4, %eax
    subl %eax, %esp
    movl %esp, %eax
    pushl %ecx
    pushl %edx
    .Jret:
    ret
    .align 8
    .type alloca,@function
    .size alloca,.-alloca

Erzeugung von alloca.o:  [i]cc -c -belf alloca.s

C-Prototyp:  void *alloca(unsigned);

Stack layout:

    1 size-arg
    2 return address
    3 alloc
    4 alloc
    5 alloc
    6 size-arg
    7 return address

Die Funktion ist defensiv angelegt, dadurch, daß 8 Byte (1+2) auf dem Stack belassen werden,
die man sicher (2) und wahrscheinlich (1) für (3) und (4) verwenden könnte.
(7) wird durch 'ret' und (6) durch den Compiler (im caller) eliminiert.

Tabelle der Warnmeldungen bei der gcc281-Kompilierung

Natürlich können die allermeisten Warnungen getrost ignoriert werden, jedoch würde ich
denen des ersten gezeigten Abschnittes anstelle der Programmierer mal nachgehen.

Ich selbst würde beim gegebenen Quelltext-Umfang höchstens 300 Meldungen so stehen lassen...

    5 warning: argument `' might be clobbered by `longjmp' or `vfork'
8 warning: variable `' might be clobbered by `longjmp' or `vfork'
69 warning: pointer of type `void*' used in arithmetic
3 warning: pointer of type `' used in subtraction
99 warning: `' might be used uninitialized in this function
6 warning: array subscript has type `'
6 warning: wrong type argument to increment
20 warning: unsigned int format, long unsigned int arg ()
14 warning: int format, long int arg ()
18 warning: `' declared `static' but never defined
24 warning: statement with no effect
8 warning: control reaches end of non-void function
4 warning: format argument is not a pointer ()
4 warning: missing braces around initializer for `'
4 warning: value computed is not used
2 warning: `' with no value, in function returning non-void
2 warning: zero-length format string

47 warning: `' defined but not used
134 warning: no previous prototype for `'
168 warning: label `' defined but not used
260 warning: unused variable `'
780 warning: implicit declaration of function `'
1847 warning: enumeration value `' not handled in switch
19911 warning: function declaration isn't a prototype

Die 6 Assembler-Ausgaben:

        .file       "w1.c"
.version "01.01"
gcc2_compiled.:
__gnu_compiled_c:
.data
.LC0:
.string "Benutze exit\n"
.text
.align 8
.globl Input
Input:
pushl %ebx
movl 8(%esp),%ebx
.L2:
movl $0,PSnu
pushl %ebx
call List
movl %eax,%edx
addl $4,%esp
testb $2,%bl
je .L6
incl KDOnu
.L6:
cmpb $0,O+116
jne .L3
cmpl $254,%edx
jle .L2
testb $2,%bl
je .L3
cmpl $520,%edx
jle .L8
testb $4,G+4
je .L2
cmpl $500,%edx
jne .L2
.L8:
cmpl $255,%edx
jne .L3
movb O+73,%al
orb O+80,%al
je .L3
pushl $14
pushl $.LC0
pushl $2
call write
addl $12,%esp
decl KDOnu
jmp .L2
.L3:
movl %edx,%eax
popl %ebx
ret
.ident "GCC: (GNU) 2.7.2.3"
-------------------------------------------------------------------
        .file        "w2.c"
.version "01.01"
gcc2_compiled.:
__gnu_compiled_c:
.data
.LC0:
.string "Benutze exit\n"
.text
.align 8
.globl Input
Input:
pushl %ebx
movl 8(%esp),%ebx
.L2:
movl $0,PSnu
pushl %ebx
call List
movl %eax,%edx
addl $4,%esp
testb $2,%bl
je .L5
incl KDOnu
.L5:
cmpb $0,O+116
jne .L3
cmpl $254,%edx
jle .L2
testb $2,%bl
je .L3
cmpl $520,%edx
jle .L10
testb $4,G+4
je .L2
cmpl $500,%edx
jne .L2
.L10:
cmpl $255,%edx
jne .L3
movb O+73,%al
orb O+80,%al
je .L3
pushl $14
pushl $.LC0
pushl $2
call write
decl KDOnu
addl $12,%esp
jmp .L2
.L3:
movl %edx,%eax
popl %ebx
ret
.ident "GCC: (GNU) 2.7.2.3"
--------------------------------------------------------------------------
        .ident "SCO Optimizing C Compiler Release 5 Version 2.1.4 P96261"
.file "w1.c"
.data
.align 16
.bss
.align 16
.text
.align 16
.globl Input
Input:
.B1.2:
pushl %edi
pushl %esi
pushl %ebp
pushl %ebx
movl 20(%esp), %ebp
subl $20, %esp
movl $14, %eax
movl %ebp, %ebx
andl $2, %ebx
xorl %esi, %esi
movl %eax, 16(%esp)
movl $_.1STRINGPACKET.1, %edi
.B1.3:
movl %esi, PSnu
movl %ebp, (%esp)
call List
.B1.4:
testl %ebx, %ebx
je .B1.6
.B1.5:
incl KDOnu
.B1.6:
movb O+116, %dl
testb %dl, %dl
jne .B1.19
.B1.7:
cmpl $255, %eax
jl .B1.3
.B1.8:
testl %ebx, %ebx
je .B1.19
.B1.9:
cmpl $520, %eax
jle .B1.12
.B1.10:
movl G+4, %edx
testl $4, %edx
je .B1.3
.B1.11:
cmpl $500, %eax
jne .B1.3
.B1.12:
cmpl $255, %eax
jne .B1.19
.B1.13:
movb O+73, %dl
movb O+80, %dh
orb %dl, %dh
je .B1.19
.B1.15:
movl 16(%esp), %eax
movl $2, (%esp)
movl %edi, 4(%esp)
movl %eax, 8(%esp)
call write
.B1.16:
decl KDOnu
jmp .B1.3
.B1.19:
addl $20, %esp
popl %ebx
popl %ebp
popl %esi
popl %edi
ret
.align 16
.data
_.1STRINGPACKET.1:
.string "Benutze exit\n"
.set .2.1_2ab_p.1, 0x0
.set .2.1_2pab_p.1, 0x0
.text
.set _2.1_2auto_size, 0x14
.data
.text
--------------------------------------------------------------------------
        .ident "SCO Optimizing C Compiler Release 5 Version 2.1.4 P96261"
.file "w2.c"
.data
.align 16
.bss
.align 16
.text
.align 16
.globl Input
Input:
.B1.2:
pushl %edi
pushl %esi
pushl %ebp
pushl %ebx
movl 20(%esp), %ebp
subl $20, %esp
movl $14, %eax
movl %ebp, %ebx
andl $2, %ebx
xorl %esi, %esi
movl %eax, 16(%esp)
movl $_.1STRINGPACKET.1, %edi
.B1.3:
movl %esi, PSnu
movl %ebp, (%esp)
call List
.B1.4:
testl %ebx, %ebx
je .B1.6
.B1.5:
incl KDOnu
.B1.6:
movb O+116, %dl
testb %dl, %dl
jne .B1.20
.B1.7:
cmpl $255, %eax
jl .B1.3
.B1.8:
testl %ebx, %ebx
je .B1.20
.B1.9:
cmpl $520, %eax
jle .B1.12
.B1.10:
movl G+4, %edx
testl $4, %edx
je .B1.3
.B1.11:
cmpl $500, %eax
jne .B1.3
.B1.12:
cmpl $255, %eax
jne .B1.20
.B1.13:
movb O+73, %dl
movb O+80, %dh
orb %dl, %dh
je .B1.20
.B1.14:
movl 16(%esp), %eax
movl $2, (%esp)
movl %edi, 4(%esp)
movl %eax, 8(%esp)
call write
.B1.15:
decl KDOnu
jmp .B1.3
.B1.20:
addl $20, %esp
popl %ebx
popl %ebp
popl %esi
popl %edi
ret
.align 16
.data
_.1STRINGPACKET.1:
.string "Benutze exit\n"
.set .2.1_2ab_p.1, 0x0
.set .2.1_2pab_p.1, 0x0
.text
.set _2.1_2auto_size, 0x14
.data
.text
--------------------------------------------------------------
        .file        "w1.c"
.version "01.01"
.text
.globl Input
.align 4
Input:
movl $0,PSnu
pushl %esi
movl 8(%esp),%esi
pushl %ebx
pushl %ebp
pushl %esi
call List
movl $KDOnu,%ebp
addl $4,%esp
movl %eax,%ecx
testl $2,%esi
je .L15
incl (%ebp)
.L15:
movb O+116,%al
testb %al,%al
jne .L10
cmpl $255,%ecx
jl .L17
testl $2,%esi
je .L10
cmpl $520,%ecx
jle .L19
movl G+4,%eax
testl $4,%eax
je .L18
cmpl $500,%ecx
jne .L18
.L19:
cmpl $255,%ecx
jne .L10
xorl %eax,%eax
xorl %edx,%edx
movb O+73,%al
movb O+80,%dl
orl %edx,%eax
je .L10
pushl $14
pushl $.X14
pushl $2
call write
decl (%ebp)
addl $12,%esp
.L18:
.L17:
movl %esi,%ebx
andl $2,%ebx
jmp .L8
..1:
..0:
pushl $14
pushl $.X20
pushl $2
call write
addl $12,%esp
decl (%ebp)
.L8:
movl $0,PSnu
pushl %esi
call List
addl $4,%esp
testl %ebx,%ebx
movl %eax,%ecx
je .L21
incl (%ebp)
.L21:
movb O+116,%al
testb %al,%al
jne .L23
cmpl $255,%ecx
jl .L8
testl %ebx,%ebx
je .L24
cmpl $520,%ecx
jle .L25
movl G+4,%eax
testl $4,%eax
je .L8
cmpl $500,%ecx
jne .L8
.L25:
cmpl $255,%ecx
jne .L26
xorl %eax,%eax
xorl %edx,%edx
movb O+73,%al
movb O+80,%dl
orl %edx,%eax
jne ..1
.L26:
.L24:
.L23:
.L10:
popl %ebp
popl %ebx
movl %ecx,%eax
popl %esi
ret
.ident "acomp: Release 5.1.1A 27Jul98"
.data
.align 4
.X14:
.byte 0x42,0x65,0x6e,0x75,0x74,0x7a,0x65,0x20,0x20,0x65
.byte 0x78,0x69,0x74,0x0a,0x00
.align 4
.X20:
.byte 0x42,0x65,0x6e,0x75,0x74,0x7a,0x65,0x20,0x20,0x65
.byte 0x78,0x69,0x74,0x0a,0x00
.text
.ident "optim: Release 5.1.1A 27Jul98"
------------------------------------------------------------------------
        .file       "w2.c"
.version "01.01"
.text
.globl Input
.align 4

Input:
movl $0,PSnu
pushl %esi
movl 8(%esp),%esi
pushl %ebx
pushl %ebp
pushl %esi
call List
movl $KDOnu,%ebp
movl %esi,%ebx
movl %eax,%ecx
andl $2,%ebx
jmp ..0
.L8:
testl %ebx,%ebx
je .L11
incl (%ebp)
.L11:
movb O+116,%al
testb %al,%al
jne .L10
cmpl $255,%ecx
jl .L13
testl %ebx,%ebx
je .L10
cmpl $520,%ecx
jle .L20
movl G+4,%eax
testl $4,%eax
je .L15
cmpl $500,%ecx
jne .L15
.L20:
cmpl $255,%ecx
jne .L10
xorl %eax,%eax
xorl %edx,%edx
movb O+73,%al
movb O+80,%dl
orl %edx,%eax
jne .L16
.L10:
popl %ebp
popl %ebx
movl %ecx,%eax
popl %esi
ret
.L16:
pushl $14
pushl $.X22
pushl $2
call write
decl (%ebp)
addl $12,%esp
.L15:
.L13:
movl $0,PSnu
pushl %esi
call List
movl %eax,%ecx
..0:
addl $4,%esp
jmp .L8
.ident "acomp: Release 5.1.1A 27Jul98"
.data
.align 4
.X22:
.byte 0x42,0x65,0x6e,0x75,0x74,0x7a,0x65,0x20,0x20,0x65
.byte 0x78,0x69,0x74,0x0a,0x00
.text
.ident "optim: Release 5.1.1A 27Jul98"