Schieb die mal nach oben oberhalb der main(). Normalerweise sollte das
gar nicht kompilieren, Du kannst eine Funktion erst aufrufen wenn der
Compiler die Stelle an der sie deklariert wurde schon gesehen hat. Also
nach oben damit. (oder hast Du forward-Deklarationen dafür?)
Peter II schrieb:> Bernd K. schrieb:>> Normalerweise sollte das>> gar nicht kompilieren>> In C ist das zulässig.
Seit wann? Mein gcc meckert jedenfalls.
Peter II schrieb:> Bernd K. schrieb:>> Normalerweise sollte das>> gar nicht kompilieren>> In C ist das zulässig.
Na und? Kommt trotzdem gerne Müll raus.
Oder hast du ne Kristallkugel, die anhand von
1
intmain(void)
2
{
3
foo(1);
4
return0;
5
}
erkennt, wie z.B. ein anderes Modul foo implementiert und diese Funktion
die Argumente erwartet? Z.B. als:
1
voidfoo(int);
2
voidfoo(long);
3
voidfoo(float);
4
voidfoo(constchar*,...);
5
voidfoo(structxyz);
6
structxyzfoo(int);
Übrigens erwartet foo im letzten Fall i.d.R. 2 Parameter.
Johann L. schrieb:> Oder hast du ne Kristallkugel
nein, aber es ist ja zum glück Definiert wie sich er Compiler zu
verhalten hat. Und die Anzahl der Paramter ist ziemlich egal, sonst
würde ein Printf mit variablen Anzahl nicht funktionieren.
Peter Zz schrieb:> das __inline ist für die Funktionen Lauf_rechts() und Lauf_links> zwingend erforderlich, sonst leuchtet nix!> Wie kann das sein?
Stacküberlauf vielleicht? Das Ding hat immerhin nur 32 Bytes RAM.
Peter II schrieb:> Johann L. schrieb:>> Oder hast du ne Kristallkugel>> nein, aber es ist ja zum glück Definiert wie sich er Compiler zu> verhalten hat.
Daß der Compiler sich definiert verhält hilft nix wenn er sich in Modul
A anders definiert verhält als in Modul B. Siehe Beispiel.
> Und die Anzahl der Paramter ist ziemlich egal, sonst> würde ein Printf mit variablen Anzahl nicht funktionieren.
Für den Compiler ist ein Funktionsaufruf ohne Prototyp nicht als
varargs-Funktion erkennbar — wie auch.
Ganz nebenbei werden die Argumente anders übergaben auch wenn es sich
um die gleichen Typen handelt. foo (x) mit int x übergibt x in R24/25
oder auf dem Stack, je nachdem. Das ist schon ein kleiner Unterschied.
Aber auch ohne varargs werden Argumente anders übergeben: foo(int x)
erwartet x in R24-25. foo(long x) erwartet x in R22-25 mit LSB in R24
bzw. R22. Und foo(float) verwendet zwar die gleichen Register wie
foo(long), interpretiert dise aber etwas anders.
Ergo: Ob der Compiler sich definiert verhält ist total Banane. Was
zählt ist ob er richtigen Code generiert, und das kann er nur mit
korrekten Prototypen. Zumindest bei avr-gcc um den es hier ja geht.
Johann L. schrieb:> Ergo: Ob der Compiler sich definiert verhält ist total Banane. Was> zählt ist ob er richtigen Code generiert, und das kann er nur mit> korrekten Prototypen. Zumindest bei avr-gcc um den es hier ja geht.
in vielen Fällen stimmt das, aber nicht in allen. In diesem Fall hier
werde keine Parameter übergeben, damit ist der Aufruf eindeutig.
Peter Zz schrieb:> ATtiny10>> das __inline ist für die Funktionen Lauf_rechts() und Lauf_links> zwingend erforderlich, sonst leuchtet nix!> Wie kann das sein?
Schau mal den erzeugten Code an bzw. Mapfile. Kann sein daß dem
Hänfling die Resourcen ausgehen, z.B. Stack?
Johann L. schrieb:> Kann sein daß dem> Hänfling die Resourcen ausgehen, z.B. Stack?
habe ich auch vermutet.
Habe dann meine Tabelle die 18 von den 32Byte RAM frisst ins Flash
gemapt.
Peter Zz schrieb:> Johann L. schrieb:>> Kann sein daß dem Hänfling die Resourcen ausgehen,>> z.B. Stack?>> habe ich auch vermutet.> Habe dann meine Tabelle die 18 von den 32Byte RAM frisst ins Flash> gemapt.>
> Und schon ein neues Problem, ich bekomme beim Compilieren zwei> Fehlermeldungen:> Register not supported> illegal opcode elpm for mcu attiny10
Ok. Dann hat die Toolchain keinen Support für ATtiny. Bestenfalls kann
man von der Toolchain sagen, daß sie für ATtiny aufgebohrt wurde...
Wenn die Toolchain ATtiny gscheit unterstützt dann sollte ein
1
staticconstuint8_tWave_Tab[18]={...};
vollkommen ausreichen:
1) Der Compiler legt diese Daten nach .rodata.
2) WIMRE mappt ATtiny Flash in den Adressbereich von LD/ST, nämlich ab
0x4000.
3) Das Linkerscript sollte .rodata also ab 0x4000 legen.
Ergo: Wenn die Toolchain ATtiny unterstützt dann brauch't das ganze
__flash oder progmem Geraffel nicht mehr.
Evtl. kann man mit eigenem Linkerscript nachhelfen das .rodata und
andere Daten, die nur gelesen werden müssen, ab 0x4000 lokatiert,
beispielsweise .ctors und .dtors.
Johann L. schrieb:> Aber auch ohne varargs werden Argumente anders übergeben: foo(int x)> erwartet x in R24-25. foo(long x) erwartet x in R22-25 mit LSB in R24> bzw. R22. Und foo(float) verwendet zwar die gleichen Register wie> foo(long), interpretiert dise aber etwas anders.
Natürlich werden unterschiedliche Parametertypen unterschiedlich
übergeben.
> Ergo: Ob der Compiler sich definiert verhält ist total Banane. Was> zählt ist ob er richtigen Code generiert, und das kann er nur mit> korrekten Prototypen. Zumindest bei avr-gcc um den es hier ja geht.
In C gab es ursprünglich gar keine Prototypen, und trotzdem konnten
Compiler korrekten Code erzeugen. Und auch heute geht das noch. Man muß
dann natürlich selber darauf achten, die Daten mit richtigem Typ zu
übergeben. Da das sehr mühsam und fehlerträchtig ist, gibt es
Prototypen, aber zwingend erforderlich sind sie nicht.
Rolf Magnus schrieb:> Johann L. schrieb:>> Aber auch ohne varargs werden Argumente anders übergeben: foo(int x)>> erwartet x in R24-25. foo(long x) erwartet x in R22-25 mit LSB in R24>> bzw. R22. Und foo(float) verwendet zwar die gleichen Register wie>> foo(long), interpretiert dise aber etwas anders.>> Natürlich werden unterschiedliche Parametertypen unterschiedlich> übergeben.>>> Ergo: Ob der Compiler sich definiert verhält ist total Banane. Was>> zählt ist ob er richtigen Code generiert, und das kann er nur mit>> korrekten Prototypen. Zumindest bei avr-gcc um den es hier ja geht.>> In C gab es ursprünglich gar keine Prototypen, und trotzdem konnten> Compiler korrekten Code erzeugen.
Das lag und liegt aber nicht am Compiler sondern am ABI, das für
Parameterübergabe nicht komplizierter war als "Pack den Krempel
wortweise auf den Stack und gut is".
Hätte man bei avr-gcc auch machen können, aber zu welcher Codegüte das
geführt hätte kann sich hier jeder ausmalen. Der Teil des avr-gcc ABI,
der festlegt, wo was wann wie übergeben wird, ist 1/2 DIN A4 Seite lang
oder mehr.
> Und auch heute geht das noch.
Ja, mit entsprechendem ABI.
> Man muß dann natürlich selber darauf achten, die Daten mit> richtigem Typ zu übergeben. Da das sehr mühsam und> fehlerträchtig ist, gibt es Prototypen, aber zwingend> erforderlich sind sie nicht.
Beispiele hab ich oben schon gegeben. Konkret: Wenn der Funktionsaufruf
1
func(0);
ist, dann kannst du nicht ausdrücken, ob 0
1) in R24/25 oder
2) in R22/23 oder
3) auf dem Stack
übergeben wird, bzw. 0 wird immer gemäß 1) übergeben. Oder wie willst
du es ohne Prototyp anstellen, dass 0 in R22/23 oder auf dem Stack
übergeben wird? Hypnose?
Das sind zumindest laut avr-gcc ABI die 3 Möglichkeiten für func, den
ersten Parameter zu erhalten wenn dieser vom Typ int ist.
Das avr-gcc ABI wurde eindeutig im Hinblick auf Prototypes definiert.
Ohne sie hätte es anders ausfallen müssen. Aber auch ohne Prototypes
wäre ein ABI definierbar, das eine begrenzte Anzahl Parameter in
Registern übergibt (varargs/stdarg.h wäre dann nur etwas komplizierter).
So gibt es Stellen, an denen das bestehende ABI überhaupt nur mit
Prototypes implementierbar ist. Beispielsweise setzen so wie im ABI
definiert Varargs-Funktionen eine Prototyp-Deklaration zwingend voraus,
weshalb dein Beispiel mit Übergabe auf dem Stack eine direkte Folge der
Entscheidung bei der ABI-Definition ist. Parameter kleiner als int kann
es ohne Prototypes auch nicht geben, ebensowenig wie bei den ungenannten
Varargs-Parametern im aktuellen ABI.
Aber was du mit R22/23 meinst erschliesst sich mir nicht. Ich sehe
keinen Weg, bei dem ein "int" als erstem Parameter in R22/23 landen
könnte und von einer solchen Funktion auch dort erwartet würde.
A. K. schrieb:> Das avr-gcc ABI wurde eindeutig im Hinblick auf Prototypes definiert.> Ohne sie hätte es anders ausfallen müssen. Aber auch ohne Prototypes> wäre ein ABI definierbar, das eine begrenzte Anzahl Parameter in> Registern übergibt (varargs/stdarg.h wäre dann nur etwas komplizierter).
Die Implementierung im GCC wäre marginal aufwändiger, ein paar Zeilen
mehr und fertig ist der Lack. Der erzeugte Code wäre kleiner, schneller
-- sowohl auf Seiten des Callers als auch des Callees -- und verbrauchte
weniger Stack.
> So gibt es Stellen, an denen das bestehende ABI überhaupt nur mit> Prototypes implementierbar ist. Beispielsweise setzen so wie im ABI> definiert Varargs-Funktionen eine Prototyp-Deklaration zwingend voraus,> weshalb dein Beispiel mit Übergabe auf dem Stack eine direkte Folge der> Entscheidung bei der ABI-Definition ist.
Bei der Implementierung hat Denis so lange mit der Registerverwendung
rumgespielt, bis er die beste Codeerzeugung erreicht hatte :-)
Feinheiten wie die Parameterübergabe bei varargs hatte er bestimmt
weniger auf dem Radar, wohl unter der Annahme, dass diese vor allem von
den eh teuren printf/scanf Funktionen generiert werden. Das trifft auch
heute noch zu; nicht-stdio varargs-Funktionen bleiben Exoten.
> Aber was du mit R22/23 meinst erschliesst sich mir nicht. Ich sehe> keinen Weg, bei dem ein "int" als erstem Parameter in R22/23 landen> könnte und von einer solchen Funktion auch dort erwartet würde.
1
typedefstruct{inta[9];}S;
2
3
Sfun(inti)
4
{
5
Ss;
6
s.a[0]=i;
7
returns;
8
}
fun erwartet i in R22/23:
1
fun:
2
movw r30,r24
3
std Z+1,r23 ;; da
4
st Z,r22 ;; und da
5
ret
Der erste Parameter ist implizit und die Adresse, wo der Aufrufer das
Ergebnis haben möchte. Und auf Seiten des Callers:
1
externSyum(int);
2
3
voidbuh(S*s)
4
{
5
*s=yum(0);
6
}
buh übergibt i natürlich in R22/23, hier für ATtiny24:
1
buh:
2
push r16
3
push r17
4
push r28
5
push r29
6
in r28,__SP_L__
7
clr r29
8
subi r28,lo8(-(-18))
9
out __SP_L__,r28
10
11
movw r16,r24
12
ldi r22,0 ;; da
13
ldi r23,0 ;; und da
14
movw r24,r28
15
adiw r24,1
16
rcall yum
17
;; blabla mit memcpy und Epilog...
Damit gibt es auch 2 Möglichkeiten, an welcher Stelle im Stack eine
varargs-Funktion einen ersten int-Parameter erwartet.
Solche Rückgabewerte sind zwar jenseits des Horizonts der meisten
Anwendungen, werden aber von C und entsprechend auch vom Compiler
unterstützt.
Sogar wenn immer alle Argumente auf dem Stack übergeben werden, machen
die Fälle solcher Rückgabewerte, die ein implizites Argument erzeugen,
ohne Prototypen Probleme. Selbst wenn das ABI immer ein implizites,
erstes (Dummy-)Argument verlangte, wüsste man nicht, wieviel Daten die
aufgerufene Funktion möglicherweise ab der übergebenen Adresse ablädt
:-)