内存分类
冯诺依曼结构计算机的内存是一整块,其内部分为数据区和指令区,也称数据段和代码段。由于JVM
是用软件模拟计算机行为,所以JVM内部也会用C++表示一段代码段内存。代码段内的数据都是计算机指令。
如何发送
当指令编码完成后,需要将该指令发送到代码段以供后续CPU执行。那么JVM
是如何实现将指令发送到代码段中的呢?
回到assembler_aarch64.hpp
文件中的add
函数定义
void add(Register Rd, Register Rn, Register Rm, enum shift_kind kind, unsigned shift = 0) {
Instruction_aarch64 do_not_use(this);
set_current(&do_not_use);
f(0, 21); ;
zrf(Rd, 0), zrf(Rn, 5), zrf(Rm, 16);
op_shifted_reg(0b01011, kind, shift, 1, 0b000);
}
这里有一个Instruction_aarch64
类对象do_not_use
,参数是this
。
我们看看这个类的定义
class Instruction_aarch64 {
unsigned insn;
Assembler *assem;
public:
Instruction_aarch64(class Assembler *as) {
insn = 0;
assem = as;
}
inline ~Instruction_aarch64();
unsigned &get_insn() { return insn; }
这个类有2个成员变量,一个是insn
,该变量保存汇编指令的机器码,即add函数内部生成的值;另一个Assembler
类对象assem
,该对象关联与架构相关的汇编器。
在add
函数中,do_not_use
对象在{
之后调用构造函数,在}
之前调用析构函数。
我们来看看构造函数实现了什么功能?
在源码中
Instruction_aarch64 do_not_use(this);
构造函数传递了一个this
参数,即当前的汇编器类对象(class Assembler : public AbstractAssembler {
)。
然后在add
函数中执行如下代码,将do_not_use对象赋值给汇编器当前指令对象(current)
set_current(&do_not_use);
在析构函数中实现了
Instruction_aarch64::~Instruction_aarch64() {
assem->emit();
}
即,在析构函数中调用了当前的汇编器对象(assem
)的emit
函数
void emit() {
emit_long(current->get_insn());//获得当前指令对象的机器码值,并将其发送到代码段
}
emit_long
函数的实现如下
void emit_long(jint x) {
...
AbstractAssembler::emit_int32(x);
}
void emit_int32( int32_t x) { code_section()->emit_int32( x); }
由于AArch64
是RISC
指令,其指令长度为32位固定长度,这里可以看到emit_int32
函数实现是将32位整型值放入代码段(code_section()
)中。这里先不要纠结code_section是哪里来的,后面在将JVM
指令内存结构时会讲到。