MIPS汇编中的面向对象继承

时间:2017-01-14 10:30:43

标签: assembly compilation mips code-generation

我是计算机科学专业的学生,​​目前正在参加编译课程,我们在课程中有一个项目,用 IC 语言构建编译器,这是一个子集Java语言我在最后一部分需要生成 MIPS 程序集,而不需要从IR树分配寄存器。
我试图了解如何在 MIPS 程序集中表示对象实例。我知道我需要为每个对象实例(包含与实例相关的函数的地址)生成虚函数表
我的问题是:
我是否需要为类字段执行类似的表,因为字段也是继承的。你会怎么建议这样做? 此外,如果有人能举例说明真正的 MIPS 汇编代码对于继承类和字段的对象实例的样子,我将非常感激。
例如,MIPS代码的外观如下:

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 个答案:

答案 0 :(得分:2)

我只解决了这部分问题:

  

此外,如果有人能举例说明真正的MIPS汇编代码会是什么样子,我将非常感激

我确实以一种方式将你的例子重写为C ++,几乎没有开关的C ++编译器不会完全优化它并保留字段和调用(如果你想知道为什么使用volatile,以及其他一些事情,只是为了防止编译器生成像return value = 5, return这样的程序集... C ++编译器在优化方面往往有点烦人,当你想看到一些"示例"代码)。

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;
}

现在,如果您将其放入http://godbolt.org/并使用选项-O3 -std=c++11 -fno-loop-optimize -fno-inline将编译器设置为MIPS gcc 5.4,您将获得此输出:

$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在实际网站上,因此您还会得到彩色提示,其中部分代码属于源的哪个部分(如果这是您的目标平台,还有MIPS64编译器)。 / p>

编辑:你也应该尝试-O0选项,输出很可能与单独学生项目合理实现的内容有很大关系。

我在MIPS中不足以解释你在那里发生了什么,我也没有时间,但如果你正在制作编译器,你应该比我更好地理解它。

C ++源代码演示虚拟调用的完成方式($LVL17),非虚拟父调用($LVL18),非虚拟自调用($LVL16)和字段值访问( $LVL19)。

现在请记住,这是专业优化的工具,所以如果你以不太理想的解决方案结束,那应该没问题。还要记住,Java和C ++的编译有点不同,在Java中最终的结果不是"静态"作为C ++,你可能没有足够的信息来像C ++一样积极地进行优化,就像非虚拟函数调用只是用目标地址硬编码一样,或字段也是......

毕竟,如果它是Java,你不能指望它是最优的,对于托管运行时语言来说它很好,并且与高质量的JIT编译器一起,基本代码速度可以开启与C ++相提并论,但是一旦遇到效率低下的Java数据结构,C ++" swoooshs"超越地平线。