2017-01-14 1 views
1

ich ein Informatik Student bin, derzeit eine Compilation Kurs nehmen, haben wir ein Projekt im Verlauf eines Compilers in IC Sprache zu bauen, die eine Teilmenge der Java-Sprache ist. Ich bin im letzten Teil, wo ich MIPS Assembly ohne Registerzuweisung aus dem IR-Baum generieren muss.
Ich versuche zu verstehen, wie Objekt Instanzen in MIPS Assembly dargestellt werden. Ich weiß, dass ich eine virtuelle Funktionstabelle für jede Objektinstanz erzeugen muss (die die Adressen der Funktionen enthält, die für die Instanz relevant sind).
Meine Frage ist:
Muss ich eine ähnliche Tabelle für die Klassenfelder tun, weil Felder auch vererbt werden. Und wie würden Sie vorschlagen, es zu tun?
Außerdem wäre ich sehr dankbar, wenn jemand ein Beispiel dafür geben könnte, wie der Assembler-Code für Objektinstanzen aussehen würde, die Klassen und Felder erben.
Zum Beispiel, was aussehen würde MIPS Code wie für:Object Oriented Vererbung in MIPS Assembly

class A{ 
    int x; 
    void f(){} 
} 

class B extends A{ 
    int y; 
    void g(){} 

    void main(){ 
     A newObj = new B(); 
     newObj.f(); 
     newObj.x++; 
    } 
} 
+1

Felder können nicht virtuell sein, daher ist keine Tabelle erforderlich. Wenn Sie wissen möchten, wie der Asm aussieht, verwenden Sie einen vorhandenen Compiler. – Jester

+2

Beachten Sie, dass ein VMT nicht einmal * pro Instanz benötigt wird * - * Einmal pro Klasse * und alle Instanzen, die einen Zeiger dorthin halten, sind ausreichend. – tofro

Antwort

2

ich nur dieser Teil der Frage bin Adressierung:

Außerdem wäre ich sehr dankbar, wenn jemand ein Beispiel geben könnte aussehen würde, wie Assembler-Code realen MIPS

ich habe Ihr Beispiel in C++ neu zu schreiben in einer Weise, dass C++ Compiler mit wenigen Schaltern wird nicht opti Mize es völlig aus und halten Sie die Felder und Anrufe (wenn Sie sich wundern, warum volatile verwendet wird, und einige andere Dinge, nur um zu verhindern, Compiler zu Assembly wie return value = 5, return produzieren ... C++ Compiler neigen dazu, ein bisschen genervt gut mit Optimierungen, wenn Sie will nur einen "Beispielcode" sehen.

class A { 
    public: 
    volatile int x; 
    virtual void f(){ 
     ++x; 
    } 
}; 

class B : public A { 
    public: 
    volatile int y; 
    B(int i) { 
     y = i; 
     x = i-1; 
    } 
    virtual void f(){ 
     x += 2; 
    } 
    void g() { 
     f(); 
     x += 3; 
     ++y; 
    } 
}; 

int footest(int in) { 
    B* obj = new B(in); 
    A* obj_A_alias = obj; 
    obj_A_alias->f();  // calling B::f(), because f is virtual 
    obj->g(); 
    obj->f(); 
    obj->A::f();   // forcing A::f() call (on B instance) 
    int result = obj->x + obj->y; 
    delete obj; 
    return result; 
} 

Nun, wenn Sie diese in http://godbolt.org/ und einrichten Compiler MIPS gcc 5.4 mit Optionen -O3 -std=c++11 -fno-loop-optimize -fno-inline gesetzt wird, werden Sie diese Ausgabe erhalten:

$LFB0 = . 
A::f(): 
$LVL0 = . 
     lw  $2,4($4) 
     addiu $2,$2,1 
     sw  $2,4($4) 
     j  $31 
     nop 

$LFB7 = . 
B::f(): 
$LVL1 = . 
     lw  $2,4($4) 
     addiu $2,$2,2 
     sw  $2,4($4) 
     j  $31 
     nop 

$LFB3 = . 
A::A(): 
$LVL2 = . 
$LBB2 = . 
     lui  $2,%hi(vtable for A+8) 
     addiu $2,$2,%lo(vtable for A+8) 
     j  $31 
     sw  $2,0($4) 

$LBE2 = . 
     A::A() = A::A() 
$LFB5 = . 
B::B(int): 
$LVL3 = . 
     addiu $sp,$sp,-40 
     sw  $17,32($sp) 
     move $17,$5 
     sw  $31,36($sp) 
     sw  $16,28($sp) 
$LBB3 = . 
     jal  A::A() 
     move $16,$4 

$LVL4 = . 
     addiu $2,$17,-1 
$LBE3 = . 
     lw  $31,36($sp) 
$LBB4 = . 
     sw  $17,8($16) 
     sw  $2,4($16) 
     lui  $2,%hi(vtable for B+8) 
$LBE4 = . 
     lw  $17,32($sp) 
$LVL5 = . 
$LBB5 = . 
     addiu $2,$2,%lo(vtable for B+8) 
     sw  $2,0($16) 
$LBE5 = . 
     lw  $16,28($sp) 
$LVL6 = . 
     j  $31 
     addiu $sp,$sp,40 

     B::B(int) = B::B(int) 
$LFB8 = . 
B::g(): 
$LVL7 = . 
     lw  $2,0($4) 
     addiu $sp,$sp,-32 
     sw  $16,24($sp) 
     sw  $31,28($sp) 
     lw  $25,0($2) 
     jalr $25 
     move $16,$4 

$LVL8 = . 
     lw  $2,4($16) 
     lw  $31,28($sp) 
     addiu $2,$2,3 
     sw  $2,4($16) 
     lw  $2,8($16) 
     addiu $2,$2,1 
     sw  $2,8($16) 
     lw  $16,24($sp) 
$LVL9 = . 
     j  $31 
     addiu $sp,$sp,32 

$LFB9 = . 
footest(int): 
$LVL10 = . 
     lui  $28,%hi(__gnu_local_gp) 
     addiu $sp,$sp,-32 
     addiu $28,$28,%lo(__gnu_local_gp) 
     sw  $16,24($sp) 
     move $16,$4 
$LVL11 = . 
     sw  $31,28($sp) 
     lw  $25,%call16(operator new(unsigned int))($28) 
1:  jalr  $25 
     li  $4,12     # 0xc 

$LVL12 = . 
     move $5,$16 
     move $16,$2 
$LVL13 = . 
     jal  B::B(int) 
     move $4,$2 

$LVL14 = . 
$LVL15 = . 
     jal  B::f() 
     move $4,$16 

$LVL16 = . 
     jal  B::g() 
     move $4,$16 

$LVL17 = . 
     lw  $2,0($16) 
     lw  $25,0($2) 
     jalr $25 
     move $4,$16 

$LVL18 = . 
     jal  A::f() 
     move $4,$16 

$LVL19 = . 
     move $4,$16 
     lw  $28,16($sp) 
     lw  $2,4($16) 
     lw  $16,8($16) 
$LVL20 = . 
     lw  $25,%call16(operator delete(void*))($28) 
$LVL21 = . 
1:  jalr  $25 
     addu $16,$2,$16 

$LVL22 = . 
     move $2,$16 
     lw  $31,28($sp) 
     lw  $16,24($sp) 
$LVL23 = . 
     j  $31 
     addiu $sp,$sp,32 

typeinfo name for A: 
     .ascii "1A\000" 
typeinfo for A: 
     .word vtable for __cxxabiv1::__class_type_info+8 
     .word typeinfo name for A 
typeinfo name for B: 
     .ascii "1B\000" 
typeinfo for B: 
     .word vtable for __cxxabiv1::__si_class_type_info+8 
     .word typeinfo name for B 
     .word typeinfo for A 
vtable for A: 
     .word 0 
     .word typeinfo for A 
     .word A::f() 
vtable for B: 
     .word 0 
     .word typeinfo for B 
     .word B::f() 

Try it auf dem tatsächlichen Ort, so dass Sie bekommen auch farbige Hinweise, welcher Teil des Codes zu welchem ​​Teil der Quelle gehört (und es gibt auch MIPS64-Compiler, wenn dies Ihre Zielplattform ist).


EDIT: auch Sie sollten wahrscheinlich zu -O0 Option versuchen, wird diese Ausgabe sehr wahrscheinlich viel mehr aufeinander bezogene, was Sie mit einem Schüler-alone-Projekt vernünftig erreichen kann.


Ich bin nicht gut genug, um in MIPS Ihnen zu erklären, was dort vor sich geht, noch habe ich Zeit dafür haben, aber wenn Sie Compiler produzieren, sollten Sie es besser ist als ich verstehen.

Die C++ Quelle demonstriert, wie virtueller Aufruf erfolgt ($LVL17), nicht virtueller Mutter Anruf ($LVL18), nicht virtueller Selbstanruf ($LVL16) und Feldwerte Zugang ($LVL19).

Denken Sie daran, dies ist ein professionelles optimiertes Tool. Wenn Sie mit einer weniger optimalen Lösung enden, sollte es in Ordnung sein. Bedenken Sie auch, dass sich die Kompilierung von Java und C++ ein wenig unterscheidet, in Java ist das Endergebnis nicht so "statisch" wie C++, also könnte es sein, dass Sie nicht genug Informationen haben, um so aggressiv zu optimieren wie C++ Virtuelle Funktionsaufrufe werden einfach mit der Zieladresse oder auch mit Feldern programmiert ...

Wenn es Java ist, kann man nicht erwarten, dass es optimal ist, für eine verwaltete Runtime-Sprache ist es gut und zusammen mit hoher Qualität JIT-Compiler die grundlegende Code-Geschwindigkeit kann auf Augenhöhe mit C++ sein, aber sobald Sie von ineffizienten Java-Datenstrukturen getroffen werden, C++ "shoooshs" entlang und jenseits von Horizont.