Forum: Compiler & IDEs Stacknutzung bei Ellipsen erzwingen (PowerPC)


von Joerg W. (joergwolfram)


Lesenswert?

Hallo,

beim Versuch, ein funktionierendes printf für den VLE-Port zu schreiben, 
bin ich auf folgendes Problem gestoßen:
Die Parameter werden alle in Registern übergeben aber va_arg greift auf 
den Stack zu, was irgendwelche Werte zur Ausgabe bringt, nur nicht die 
übergebenen. Das passiert auch bei -O0. Meine gegenwärtige Lösung ist 
ein kleines "Scratch RAM" oberhalb des Stacks, in das ich blind eine 
Anzahl von Registern per Inline-ASM schreibe und dann dort meinen 
Parameter-Pointer hinsetze. Das funktioniert auch und mit einer 
maximalen Anzahl von Parametern kann ich auch leben. Aber eine elegante 
Lösung ist es nicht gerade.
Gibt es eine Möglichkeit, den GCC zur Parameterübergabe per Stack in 
solchen Situationen zu zwingen? Oder lässt sich das irgendwie in den GCC 
reinpatchen?

Jörg

von Klaus W. (mfgkw)


Lesenswert?

Ist die Funktion beim Aufruf korrekt deklariert (mit ...)?

Dann hätte ich erwartet, daß alles in diesem Bereich eben nicht per 
Register übergeben wird.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Es ergibt eigentlich kein Sinn, dass der Compiler versucht Parameter via 
Register an eine Funktion mit Ellipse zu übergeben. Zeig doch einfach 
mal ein kleines Beispiel.

von Joerg W. (joergwolfram)


Lesenswert?

Beispiel:
1
unsigned int x,y,z;
2
x=0x5555;
3
y=0x6666;
4
z=0x7777;
5
printf("values = %x %x %x",x,y,z);
Das aufrufende Programm packt den Pointer auf den String in R3 und die 
Parameter 0x5555, 0x6666, 0x7777 in die Register R4,R5 und R6.
1
void printf (const char *format, ...)
2
{
3
    va_list         pointer;
4
    unsigned char   signed_argument;
5
    value_t         value;
6
    unsigned char   mode;
7
    char            c;
8
    int             temp;
9
10
    va_start(pointer,format);
Hier wird auch format in R3 übernommen, nach va_start zeigt aber der 
Pointer auf den Stack und will mit va_arg auch die Parameter vom Stack 
holen, was natürlich schief geht. Ich helfe mir jetzt mit:
1
void printf (const char *format, ...)
2
{
3
    va_list         pointer;
4
    unsigned char   signed_argument;
5
    value_t         value;
6
    unsigned char   mode;
7
    char            c;
8
    int             temp;
9
10
    pointer=0x40007fc0;
11
12
    asm volatile("e_lis      28,0x4000");
13
    asm volatile("e_or2i     28,0x7FC0");
14
    asm volatile("se_stw     4,0(28)");
15
    asm volatile("se_stw     5,4(28)");
16
    asm volatile("se_stw     6,8(28)");
17
    asm volatile("se_stw     7,12(28)");
18
    asm volatile("e_stw      8,16(28)");
19
    asm volatile("e_stw      9,20(28)");
20
    asm volatile("e_stw      10,24(28)");
21
    asm volatile("e_stw      11,28(28)");
wobei ich im Startup den Stack unterhalb von 0x40007FC0 initialisiere. 
Ab dieser Stelle kann ich mir dann auch die Parameter mittels 
va_arg(pointer,xxx) holen, halt maximal 8 Stück (sofern man keine 
Doubles nutzt).

Jörg

von Markus F. (mfro)


Lesenswert?

Hat das aufrufende Programm den Funktionsprototypen gesehen?

Das muß auch ohne solche Verrenkungen funktionieren.

von Joerg W. (joergwolfram)


Lesenswert?

Ja, den kennt es, wenn ich den Prototypen im Header auskommentiere, 
bekomme ich die übliche "implicit declaration..." Warnung.

Der Compiler ist ein 4.8.0 mit diversen Patches aus dem Internet, ein 
offizielles Release für PowerPC it VLE habe ich nirgendwo gefunden. 
Ähnliche Probleme hatte ich auch beim Renesas 78K0R und beim Freescale 
S12X. Um den konstanten Format-String NUR im Flash zu haben, scheinen 
auf den meisten Plattformen irgendwelche "Verrenkungen" notwendig zu 
sein.

Ziel ist eine Bibliothek, die auf allen möglichen Plattformen das selbe 
oder zmindest nachvollziehbar ähnliches Verhalten zeigt, der folgende 
Codeausschnitt ist nur ein Beispiel:
1
    set_printf_output(OUTPUT_UART0);
2
    printf("VAL = %i",var);

Jörg

von Markus F. (mfro)


Lesenswert?

Dann kann natürlich noch sein, daß dein stdarg.h "kaputt" ist.

Kannst Du mal ausprobieren, ob dein Compiler was mit
1
__attribute__((stdcall))
 oder
1
cdecl
 anfangen kann? Eins von beiden erzwingt möglicherweise die 
Parameterübergabe über den Stack.

von Peter D. (peda)


Lesenswert?

Viele Compiler versuchen, zu optimieren und nicht alles stur über 
Pointer zu übergeben. Speziell bei RISC kann ja nicht direkt im RAM 
gerechnet werden, sondern muß erstmal in Register zurück geladen werden.
Oft landen daher die ersten 3 Argumente direkt in Registern und nur 
darüber wird ein Pointer bemüht.
Das printf der Standard-Lib des Compilers weiß das und verhält sich 
entsprechend.

von Joerg W. (joergwolfram)


Lesenswert?

Mein stdarg.h sieht so aus, das sollte eigentlich passen:
1
typedef unsigned char * va_list;
2
#define va_start(marker, last)  { marker = (va_list)&last + sizeof(last); }
3
#define va_arg(marker, type)    *((type *)((marker += sizeof(type)) - sizeof(type)))
4
#define va_copy(dest, src)      { dest = src; }
5
#define va_end(marker)          { marker = (va_list) 0; };

_cdecl kennt der Compiler nicht und __attribute__((stdcall)) behebt das 
Problem auch nicht. Es funktioniert ja so, anders wäre es mir aber 
lieber gewesen.

Eine Standard-Lib gibt es nicht, da es bis dato noch nicht einmal ein 
funktionierendes Compiler-Binary zum Download gab. Mein printf ist eine 
Variante, die für die Ausgabe einen Funktionspointer nutzt. Damit kann 
ich dieselben Routinen für UART, SPI, Speicher (Strings), LCD, Video 
etc. verwenden.

Jörg

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


Lesenswert?

Dumme Idee. Änderts was, wenn du deine printf() nicht printf() sondern 
z.B. my_printf() nennst?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:
> Mein stdarg.h sieht so aus, das sollte eigentlich passen:
>
1
> typedef unsigned char * va_list;
2
> #define va_start(marker, last)  { marker = (va_list)&last + 
3
> sizeof(last); }
4
> #define va_arg(marker, type)    *((type *)((marker += sizeof(type)) - 
5
> sizeof(type)))

Mit Verlaub, das ist Hack mit Soße...  wie wär's z.B. mit
1
#define va_start(v,l)  __builtin_va_start(v,l)
usw.?

Joerg Wolfram schrieb:
> Der Compiler ist ein 4.8.0 mit diversen Patches aus dem Internet,

Und die Autoren wissen was sie tun?

von Joerg W. (joergwolfram)


Lesenswert?

Die stdarg.h ist vom SDCC, vielleicht ist das das Problem. Aber damit 
war es mir immerhin möglich, Funktionen plattformübergreifend zu 
implementieren.

>Und die Autoren wissen was sie tun?

Welche Autoren? Wie ich schon weiter oben geschrieben habe, habe ich 
keinen fertigen GCC für diese Architektur (Powerpc-VLE) gefunden. Anhand 
von Patches von u.a. James Lemke sowie zig SVN checkouts habe ich mir 
dann einen funktionierenden Compiler selbst "zusammengebaut". Damit habe 
ich allerdings kein lauffähiges Programm zusammenbekommen. Also: zuerst 
eigenes Startupfile mit den notwendigen Initialisierungen in ASM 
erstellt und mit -nostartfiles dazugelinkt. Und so habe ich mich Schritt 
für Schritt "vorgetastet".

Jörg

von Markus F. (mfro)


Lesenswert?

Joerg Wolfram schrieb:
> Die stdarg.h ist vom SDCC, vielleicht ist das das Problem.

aber ganz bestimmt. Nimm das, das zu deinem Compiler paßt und alles ist 
gut.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joerg Wolfram schrieb:
> Johann L. schrieb:
>>Und die Autoren wissen was sie tun?
>
> Welche Autoren?

Die Autoren der entsprechenden Patches bzw. der entsprechenden 
Erweiterungen des Compilers und der Bibliotheken und was sonst noch 
alles dazu gehört und benötigt wird.

> einen funktionierenden Compiler selbst "zusammengebaut"

Was sagen denn die Ergebnisse der Testsuite?  Da werden ja auch 
variadische Funktionen getestet.  Wenn du das halbwegs ernsthaft 
betreibst und dich entschieden hast einen entsprechenden GCC-Port zu 
maintainen (auch wenn nur für dich selbst) gehört Testing ja definitiv 
dazu.

Oder ist es lediglich ein Hobbyprojekt?

> Die stdarg.h ist vom SDCC

stdarg.h ist hoch gradig System- und ABI-abhängig.  Du kannst nicht 
erwarten dass die generisch für alle Compiler / ABIs funktioniert.

von Joerg W. (joergwolfram)


Lesenswert?

Also vielen Dank für die Hinweise, mit der stdarg.h vom GCC hat es dann 
funktioniert. Das werde ich demnächst auch bei den anderen 
Nicht-SDCC-Plattformen ausprobieren.

Das Ganze ist nur ein Hobbyprojekt und ein bisschen "Denkübung". Ziel 
ist eine Art "Universalbibliothek", mit der man auch I/O lastige 
Anwendungen problemlos und ohne größeren Aufwand zwischen verschiedenen 
Mikrocontrollerplattformen (momentan:10) portieren kann. Dazu gehören 
Clock-Einstellungen, IO-Ports, UART, SPI, ADC, PWM, effizientes 
Fliesskomma... Einen Teil davon schreibe ich in ASM, dafür mache ich 
dann auch Tests.
Vor einiger Zeit habe ich im Forum nachgefragt, ob Interesse an so einer 
Lösung besteht, aber nur eine einzige Antwort bekommen. Von daher habe 
ich erstmal von einer Veröffentlichung abgesehen.

Jörg

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.