skip to main content

kiesler.at

Codeoptimierung mit BURG
updated by rck, 2006-03-26

Im Sommersemester 2004 galt es, wie schon viele Jahre davor, im Rahmen der Übung Übersetzerbau einen codeerzeugenden Compiler zu schreiben und zu optimieren. Ich möchte hier ein paar der von mir eingesetzten naheliegenden und weniger naheliegenden Tricks vorstellen.

1 | 2 | 3 | 4 | 5 | 6 | 7

Head & Tail optimieren

Neben diesen allgemeinen Tips kann man auch noch einzelne Operationen optimieren. Head oder Tail beispielsweise hat Risewind aus dem Informatik-Forum so gelöst:

246 print_head(int rs1, int rd) {
247 
248         trace("# print_head(%d, %d)\n",
249                 rs1, rd);
250 
251         check_list(rs1);
252 
253 
254         /* wieder ein genialer Peter-Trick...
255         */
256 
257         printf("\tldq $%d, -1($%d)\n",
258                 rd, rs1);
259 
260         force_valid_list(rd);
261 }
262 
263 
264 
301 print_tail(int rs1, int rd) {
302 
303         trace("# print_tail(%d, %d)\n",
304                 rs1, rd);
305 
306         check_list(rs1);
307 
308         printf("\tldq $%d, 7($%d)\n",
309                 rd, rs1);
310 
311         force_valid_list(rd);
312 }

Der erzeugte Code:

==========> Eingabe von /usr/ftp/pub/ublu/test/code/b5.in:
hd $17

Übersetzung: Status korrekt

Ausgabe:
blbc $17, raisesig
ldq $0, -1($17)

Übersetze und linke das Testprogramm:

Rufe folgendes Codefragment auf:
return testasm(a,b,c,d) != head(b);

Erzeugter Code verhaelt sich bezueglich Signal-Erzeugung wie erwartet
und das Ergebnis des Ausdrucks ist korrekt.

==========> Eingabe von /usr/ftp/pub/ublu/test/code/b6.in:
tl $19

Übersetzung: Status korrekt

Ausgabe:
blbc $19, raisesig
ldq $0, 7($19)

Übersetze und linke das Testprogramm:

Rufe folgendes Codefragment auf:
return testasm(a,b,c,d) != tail(d);

Erzeugter Code verhaelt sich bezueglich Signal-Erzeugung wie erwartet
und das Ergebnis des Ausdrucks ist korrekt.

Not optimieren

Auch not braucht man nicht taggen/untaggen, wenn man sich den Tip von Anton Ertl zu Herzen nimmt:

184 print_not(int rs1, int rd) {
185 
186         trace("# print_not(%d, %d)\n",
187                 rs1, rd);
188 
189         check_int(rs1);
190 
191         /* Danke, Anton Ertl! :-)
192         */
193 
194         printf("\txornot $%d, 1, $%d\n",
195                 rs1, rd);
196 
197         force_valid_int(rd);
198 }

Der erzeugte Code:

==========> Eingabe von /usr/ftp/pub/ublu/test/code/b3.in:
not $16

Übersetzung: Status korrekt

Ausgabe:
blbs $16, raisesig
xornot $16, 1, $0

Übersetze und linke das Testprogramm:

Rufe folgendes Codefragment auf:
return testasm(a,b,c,d) != (~a)&(~1);

Erzeugter Code verhaelt sich bezueglich Signal-Erzeugung wie erwartet
und das Ergebnis des Ausdrucks ist korrekt.

weitere Anweisungs-Optimierungen

Im wesentlichen gibt es bei den meisten Instruktionen irgendwelche Tricks, um auf ganz wenige Opcodes zu kommen. Wer mag, kann meine Ideen dazu noch im Quelltext nachlesen -- ich stelle ihn in den kommenden Wochen ebenfalls online.

1 | 2 | 3 | 4 | 5 | 6 | 7



RSSComments - Make a comment
The comments are owned by the poster. We are not responsible for its content.
  • Prototypes

    Posted on 2004-06-03 11:43:21 By Anonymous

    changed On 2004-06-03 12:32:28 Edited By rck (reason: versehentlich original-Kommentar von Gergo durch meinen Ersetzt. Hier wieder sein Kommentar. Sorry!)

    Prototypes
    Gesendet am: 2004-06-03 11:43:21 von: Anonymous

    Im Jahr 1989 wurde die Programmiersprache C in den USA von ANSI standardisiert, 1990 wurde dieser Standard von ISO als internationale Norm verabschiedet. Schon in diesem C89-Standard galten Funktionsdefinitionen ohne Protype (d.h. ohne explizite Angabe von Rückgabe- und Parametertypen) als "obsolescent feature". Eine Definition wie "alloc_register() {" ist also bereits seit 15 Jahren veraltet!
    Der neue C-Standard, C99, läßt überhaupt keine Funktionsdefinition ohne Prototype mehr zu. Es empfiehlt sich daher, in allen neu geschriebenen Programmen auf Prototypes zu achten und die Funktion explizit als "int alloc_register(void)" zu deklarieren. Das ist nicht bloß eine Stilfrage, dadurch ermöglicht man dem Compiler ja erst anständiges type checking.
    Der Preis: Man muß etwas mehr tippen. Das sollte es uns aber schon wert sein ;-)

    Gergo

    [Reply ]

    • Re: Prototypes

      Posted on 2004-06-03 12:31:28 By rck[110]

      Hallo Gergo! Herzlichen Dank für Deinen Kommentar.

      die Funktion explizit als "int alloc_register(void)" zu deklarieren. Das ist nicht bloß eine Stilfrage, dadurch ermöglicht man dem Compiler ja erst anständiges type checking.

      hmm. Ich hab durchaus Prototypen geschrieben, in die .h Datei, wo sie hingehören. Ein Auszug:

      1 /*              the assembler codegenerator,
      2                 to keep the bfe file tidy.
      3 
      4                 http://www.kiesler.at/
      5 */
      6 
      7 
      8 int print_not(int rs1, int rd);
      9 int print_not_const(int const, int rd);
      10 
      11 int print_neg(int rs1, int rd);
      12 int print_neg_const(int const, int rd);
      13 
      14 int print_head(int rs1, int rd);
      15 int print_tail(int rs1, int rd);
      16 int print_islist(int rs, int rd);
      17 
      18 int print_add(int rs1, int rs2, int rd);
      19 int print_add_const(int rs1, int const, int rd);
      20 int print_add_const_const(int const1, int const2, int rd);
      21 
      22 int print_mul(int rs1, int rs2, int rd);
      23 int print_mul_const(int rs1, int const, int rd);
      24 int print_mul_const_const(int const1, int const2, int rd);
      25 
      26 int print_add(int rs1, int rs2, int rd);
      27 int print_add_const(int rs1, int const, int rd);
      28 int print_add_const_const(int const1, int const2, int rd);


      ist das lesbarer als:

      17 /*              arithmetics
      18 
      19                 our mini-compiler only supports
      20                 add, negate and multiply.
      21 
      22 */
      23 
      24 node_add(treenodep a, treenodep b, treenodep dest);
      25 node_mul(treenodep a, treenodep b, treenodep dest);
      26 node_neg(treenodep a, treenodep b, treenodep dest);


      ? Stelle ich zur Diskussion, imho nicht. "Richtig" wäre wahrscheinlich sogar, statt der ints überall ein void hinzuschreiben. Dem Compiler ist das aber Wurst.

      Gleichzeitig stelle ich mir die Frage: Ändert das was an der Wartbarkeit? Habe ich davon einen Mehrwert? Wird dadurch mein erzeugter Code schneller? Was hab ich überhaupt davon, wenn ich explizit einen Rückgabetyp hinschreiben?

      Ich persönlich habe lieber übersichtlichen, kompakten Code. Vergleiche zB mal das Mini-Tutorial zu BURG mit folgendem Snippet aus meinem Code:

      22 start:  ARETURN(regnr)          # 1 #   node_return(LEFT_CHILD(this));
      23 
      24 regnr:  AADD(regnr, regnr)      # 3 #   node_add(LEFT_CHILD(this), RIGHT_CHILD(this), this);
      25 regnr:  AAND(regnr, regnr)      # 3 #   node_and(LEFT_CHILD(this), RIGHT_CHILD(this), this);
      26 regnr:  AMUL(regnr, regnr)      # 3 #   node_mul(LEFT_CHILD(this), RIGHT_CHILD(this), this);


      ...was ist übersichtlicher?

      Bezüglich seit 15 Jahren obsolet: Ja, vielleicht. Eigentlich sollte MS-DOS auch seit ewig obsolet sein, jeder neue AMD64 Rechner hat trotzdem noch das dazu passende A20-Gate. Irgendwo eine philosophische Frage... // René!

      [Reply ]

      • Re: Prototypes

        Posted on 2004-06-03 13:20:35 By Anonymous

        changed On 2004-06-03 13:33:08 Edited By rck (reason: Code lesbarer gemacht)

        Du argumentierst leider etwas an meiner beabsichtigten Richtung vorbei: Ja, ob man jetzt explizit "int" als Rueckgabewert hinschreibt oder nicht, ist ziemlich egal.
        Der Mehrwert von Prototypes kommt aber nicht vom Rueckgabewert, sondern von den Parametern.
        Nimm zum Beispiel dieses Programm:


        1 #include <stdio.h>
        2 
        3 func_ohne_args()
        4 {
        5         puts("hallo, arglose welt!");
        6 }
        7 
        8 func_nimmt_float(f)
        9         float f;
        10 {
        11         printf("f ist: %f\n", f);
        12 }
        13 
        14 int main(void)
        15 {
        16         func_ohne_args();
        17         func_ohne_args(1);
        18         func_ohne_args(1,2);
        19 
        20         func_nimmt_float();
        21         func_nimmt_float(12345);
        22         func_nimmt_float("kernighan und ritchie");
        23         func_nimmt_float(3.14159);
        24         func_nimmt_float(3.14159, 2.7172);
        25 
        26         return 0;
        27 }

        und sag mir, ob du das so wartbar findest. (Und ob du ueber den Output ebenso erstaunt bist wie ich!) Mit anstaendigen Prototypen wuerden die falschen Aufrufe sofort vom Compiler erkannt werden, aber in C89 ist der Compiler dazu nicht verpflichtet (GCC tut's auch nicht). Ohne type checking stehst du ziemlich allein da.
        Und ja, es bringt auch einen Mehrwert, sich an Standards zu halten.

        Gergo

        [Reply ]

RSSAll Articles
2008, 2007, 2006, 2005, 2004