MIPS 汇编中的面向对象继承
Object Oriented Inheritance in MIPS Assembly
我是一名计算机科学专业的学生,目前正在上编译课程,我们在课程期间有一个项目要在IC[=26中构建一个编译器=] 语言是 Java 语言的子集。我在最后一部分需要生成 MIPS 程序集,而无需从 IR 树分配寄存器。
我试图了解对象实例在 MIPS 程序集中的表示方式。我知道我需要为每个对象实例生成一个 Virtual Function Table(其中包含与实例相关的函数的地址)。
我的问题是:
我是否需要为 class 字段做类似的 table,因为字段也是继承的。你建议怎么做?
此外,如果有人能举例说明继承 classes 和字段的对象实例的真实 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++;
}
}
我只解决这部分问题:
Furthermore, I would be very grateful if anyone could give an example of how real MIPS assembly code would look
我确实以某种方式将您的示例重写为 C++,具有少量开关的 C++ 编译器不会完全优化它并保留字段和调用(如果您想知道为什么使用 volatile
,以及其他一些事情,只是为了防止编译器生成像 return value = 5, return
这样的程序集...当您只想看到一些 "example" 代码时,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 ,4()
addiu ,,1
sw ,4()
j
nop
$LFB7 = .
B::f():
$LVL1 = .
lw ,4()
addiu ,,2
sw ,4()
j
nop
$LFB3 = .
A::A():
$LVL2 = .
$LBB2 = .
lui ,%hi(vtable for A+8)
addiu ,,%lo(vtable for A+8)
j
sw ,0()
$LBE2 = .
A::A() = A::A()
$LFB5 = .
B::B(int):
$LVL3 = .
addiu $sp,$sp,-40
sw ,32($sp)
move ,
sw ,36($sp)
sw ,28($sp)
$LBB3 = .
jal A::A()
move ,
$LVL4 = .
addiu ,,-1
$LBE3 = .
lw ,36($sp)
$LBB4 = .
sw ,8()
sw ,4()
lui ,%hi(vtable for B+8)
$LBE4 = .
lw ,32($sp)
$LVL5 = .
$LBB5 = .
addiu ,,%lo(vtable for B+8)
sw ,0()
$LBE5 = .
lw ,28($sp)
$LVL6 = .
j
addiu $sp,$sp,40
B::B(int) = B::B(int)
$LFB8 = .
B::g():
$LVL7 = .
lw ,0()
addiu $sp,$sp,-32
sw ,24($sp)
sw ,28($sp)
lw ,0()
jalr
move ,
$LVL8 = .
lw ,4()
lw ,28($sp)
addiu ,,3
sw ,4()
lw ,8()
addiu ,,1
sw ,8()
lw ,24($sp)
$LVL9 = .
j
addiu $sp,$sp,32
$LFB9 = .
footest(int):
$LVL10 = .
lui ,%hi(__gnu_local_gp)
addiu $sp,$sp,-32
addiu ,,%lo(__gnu_local_gp)
sw ,24($sp)
move ,
$LVL11 = .
sw ,28($sp)
lw ,%call16(operator new(unsigned int))()
1: jalr
li ,12 # 0xc
$LVL12 = .
move ,
move ,
$LVL13 = .
jal B::B(int)
move ,
$LVL14 = .
$LVL15 = .
jal B::f()
move ,
$LVL16 = .
jal B::g()
move ,
$LVL17 = .
lw ,0()
lw ,0()
jalr
move ,
$LVL18 = .
jal A::f()
move ,
$LVL19 = .
move ,
lw ,16($sp)
lw ,4()
lw ,8()
$LVL20 = .
lw ,%call16(operator delete(void*))()
$LVL21 = .
1: jalr
addu ,,
$LVL22 = .
move ,
lw ,28($sp)
lw ,24($sp)
$LVL23 = .
j
addiu $sp,$sp,32
typeinfo name for A:
.ascii "1A[=11=]0"
typeinfo for A:
.word vtable for __cxxabiv1::__class_type_info+8
.word typeinfo name for A
typeinfo name for B:
.ascii "1B[=11=]0"
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 编译器)。
编辑:你也应该尝试 -O0
选项,该输出很可能与你可以通过 one-student-alone 项目合理实现的目标更相关。
我在 MIPS 方面不够好,无法向您解释那里发生了什么,我也没有时间解释,但是如果您正在生产编译器,您应该比我更了解它。
C++ 源代码演示了如何完成虚拟调用 ($LVL17
)、非虚拟 parent 调用 ($LVL18
)、非虚拟自调用 ($LVL16
)和字段值访问 ($LVL19
).
请记住,这是专业的优化工具,所以如果您以不太理想的解决方案结束,应该没问题。还要记住,Java 和 C++ 的编译有点不同,在 Java 中,最终结果不像 C++ 那样 "static",所以可能是你没有足够的信息来像 C++ 一样积极优化,就像非虚函数调用只是用目标地址或字段硬编码...
毕竟,如果它是 Java,您不能指望它是最佳的,对于托管运行时语言来说,它还算不错,再加上高质量的 JIT 编译器,基本的代码速度可以提高与 C++ 相提并论,但是一旦您遇到效率低下的 Java 数据结构,C++ "swoooshs" 就会超越 horizon.
我是一名计算机科学专业的学生,目前正在上编译课程,我们在课程期间有一个项目要在IC[=26中构建一个编译器=] 语言是 Java 语言的子集。我在最后一部分需要生成 MIPS 程序集,而无需从 IR 树分配寄存器。
我试图了解对象实例在 MIPS 程序集中的表示方式。我知道我需要为每个对象实例生成一个 Virtual Function Table(其中包含与实例相关的函数的地址)。
我的问题是:
我是否需要为 class 字段做类似的 table,因为字段也是继承的。你建议怎么做?
此外,如果有人能举例说明继承 classes 和字段的对象实例的真实 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++;
}
}
我只解决这部分问题:
Furthermore, I would be very grateful if anyone could give an example of how real MIPS assembly code would look
我确实以某种方式将您的示例重写为 C++,具有少量开关的 C++ 编译器不会完全优化它并保留字段和调用(如果您想知道为什么使用 volatile
,以及其他一些事情,只是为了防止编译器生成像 return value = 5, return
这样的程序集...当您只想看到一些 "example" 代码时,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 ,4()
addiu ,,1
sw ,4()
j
nop
$LFB7 = .
B::f():
$LVL1 = .
lw ,4()
addiu ,,2
sw ,4()
j
nop
$LFB3 = .
A::A():
$LVL2 = .
$LBB2 = .
lui ,%hi(vtable for A+8)
addiu ,,%lo(vtable for A+8)
j
sw ,0()
$LBE2 = .
A::A() = A::A()
$LFB5 = .
B::B(int):
$LVL3 = .
addiu $sp,$sp,-40
sw ,32($sp)
move ,
sw ,36($sp)
sw ,28($sp)
$LBB3 = .
jal A::A()
move ,
$LVL4 = .
addiu ,,-1
$LBE3 = .
lw ,36($sp)
$LBB4 = .
sw ,8()
sw ,4()
lui ,%hi(vtable for B+8)
$LBE4 = .
lw ,32($sp)
$LVL5 = .
$LBB5 = .
addiu ,,%lo(vtable for B+8)
sw ,0()
$LBE5 = .
lw ,28($sp)
$LVL6 = .
j
addiu $sp,$sp,40
B::B(int) = B::B(int)
$LFB8 = .
B::g():
$LVL7 = .
lw ,0()
addiu $sp,$sp,-32
sw ,24($sp)
sw ,28($sp)
lw ,0()
jalr
move ,
$LVL8 = .
lw ,4()
lw ,28($sp)
addiu ,,3
sw ,4()
lw ,8()
addiu ,,1
sw ,8()
lw ,24($sp)
$LVL9 = .
j
addiu $sp,$sp,32
$LFB9 = .
footest(int):
$LVL10 = .
lui ,%hi(__gnu_local_gp)
addiu $sp,$sp,-32
addiu ,,%lo(__gnu_local_gp)
sw ,24($sp)
move ,
$LVL11 = .
sw ,28($sp)
lw ,%call16(operator new(unsigned int))()
1: jalr
li ,12 # 0xc
$LVL12 = .
move ,
move ,
$LVL13 = .
jal B::B(int)
move ,
$LVL14 = .
$LVL15 = .
jal B::f()
move ,
$LVL16 = .
jal B::g()
move ,
$LVL17 = .
lw ,0()
lw ,0()
jalr
move ,
$LVL18 = .
jal A::f()
move ,
$LVL19 = .
move ,
lw ,16($sp)
lw ,4()
lw ,8()
$LVL20 = .
lw ,%call16(operator delete(void*))()
$LVL21 = .
1: jalr
addu ,,
$LVL22 = .
move ,
lw ,28($sp)
lw ,24($sp)
$LVL23 = .
j
addiu $sp,$sp,32
typeinfo name for A:
.ascii "1A[=11=]0"
typeinfo for A:
.word vtable for __cxxabiv1::__class_type_info+8
.word typeinfo name for A
typeinfo name for B:
.ascii "1B[=11=]0"
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 编译器)。
编辑:你也应该尝试 -O0
选项,该输出很可能与你可以通过 one-student-alone 项目合理实现的目标更相关。
我在 MIPS 方面不够好,无法向您解释那里发生了什么,我也没有时间解释,但是如果您正在生产编译器,您应该比我更了解它。
C++ 源代码演示了如何完成虚拟调用 ($LVL17
)、非虚拟 parent 调用 ($LVL18
)、非虚拟自调用 ($LVL16
)和字段值访问 ($LVL19
).
请记住,这是专业的优化工具,所以如果您以不太理想的解决方案结束,应该没问题。还要记住,Java 和 C++ 的编译有点不同,在 Java 中,最终结果不像 C++ 那样 "static",所以可能是你没有足够的信息来像 C++ 一样积极优化,就像非虚函数调用只是用目标地址或字段硬编码...
毕竟,如果它是 Java,您不能指望它是最佳的,对于托管运行时语言来说,它还算不错,再加上高质量的 JIT 编译器,基本的代码速度可以提高与 C++ 相提并论,但是一旦您遇到效率低下的 Java 数据结构,C++ "swoooshs" 就会超越 horizon.