目录
背景
在一个技术群里,有一个哥们对着hotspot的源码问了个问题:
源码
看一下对应的源码:
// 来源:hotspot/src/cpu/x86/vm/assembler_x86.cpp
void Assembler::notl(Register dst) {
int encode = prefix_and_encode(dst->encoding());
emit_int8((unsigned char)0xF7);
emit_int8((unsigned char)(0xD0 | encode));
}
// 来源:hotspot/src/cpu/x86/vm/register_x86.hpp
// Register是RegisterImpl的指针类型
typedef RegisterImpl* Register;
class RegisterImpl: public AbstractRegisterImpl {
...
// encoding函数是返回RegisterImpl实例在内存中的首地址
// 也就是代表的寄存器的首地址
int encoding() const {
...
return (intptr_t)this;
}
...
}
// 来源:hotspot/src/cpu/x86/vm/assembler_x86.cpp
// 该函数输出指令前缀并返回寄存器的地址
int Assembler::prefixq_and_encode(int reg_enc) {
if (reg_enc < 8) {
// 寄存器地址小于8,输出指令前缀
prefix(REX_W);
} else {
// 寄存器地址 >=8,输出扩展指令前缀
prefix(REX_WB);
// 减8是因为只有8个通用寄存器
reg_enc -= 8;
}
// 返回寄存器的首地址
return reg_enc;
}
// 来源:hotspot/src/cpu/x86/vm/assembler_x86.cpp
// 输出8个位
void emit_int8(int8_t x) {
code_section()->emit_int8(x);
}
// 来源: hotspot/src/share/vm/asm/codeBuffer.hpp
// 汇编代码缓冲区,写入8bit
void emit_int8 (int8_t x) {
// end()位子写入x
*((int8_t*) end()) = x;
// end位置往后+8
set_end(end() + sizeof(int8_t));
}
从源码上看,理解到了HotSpot在编译执行时,生成汇编代码的实现动作。但是为什么not寄存器指令要输出 emit_int8((unsigned char)0xF7);emit_int8((unsigned char)(0xD0 | encode));
?
指令
看一下X86架构一条指令的结构:
- Instruction Prefixes:指令前缀,非必须
- Opcode:指令,必须
- ModR/M:扩展指令,非必须
- SIB:扩展指令增强,非必须
- Displacement:偏移量,非必须
- Immediate:立即数,非必须
当前not指令只用到了Opcode和ModR/M两个结构:
那输出的 0xF7,0D0 | encode
到底是什么意思呢?
解析硬编码
先不考虑 |(或)
运算
0xF7D0 = 0xF7 11 010 000
通过《Intel 开发手册》查表:
F7和010的组合代表 not 指令
11 和 000 ~ 111的组合代表具体的寄存器,或运算就是决定具体的寄存器
总结
更深入理解JVM(HotSpot)编译执行落地过程
标签:编码,x86,HotSpot,emit,编译,指令,寄存器,encode,int8 From: https://www.cnblogs.com/ylc0x01/p/17590839.html