[JVM] template interpreter-how to generate Assembly codes based on bytecode ?, Jvm Interpreter
1. Background
Template interpreter for JVM only:
How to generate an assembly code based on the opcode and addressing mode.
For the bytecode and sink encoding used in this example, see the previous blog post: pass by value or by reference?
2. Addressing Mode
This article does not intend to elaborate on the addressing mode in depth, we focus on Intel's IA32-64 architecture command format:
For more information, see the intel manual:
-Prefixes: Used to modify the Opcode and grant its lock and repeat semantics.
-REX Prefix:
-- Specify GPRs and SSE registers.
-- Specify 64-bit operand size.
-- Specify extended control registers.
-Opcode: Operation code, such as mov and push.
-Mod R/M: Addressing. For more information, see the manual.
-SIB: Combined with Mod R/M to specify addressing.
-Displacement: Used with Mod R/M and SIB to specify addressing.
-Immediate: Immediate count.
If you do not understand the Opcode, Mod R/W, SIB, disp, And imm above, you can refer to the following concepts:
%mov %eax , %rax,-0x18(%rcx,%rbx,4)
If this statement is not clear, use the following statement:
-Base + (Index scaling Scale) + Displacement-Using all the addressing components together allows efficient
Indexing of a two-dimen1_array when the elements of the array are 2, 4, or 8 bytes in size.
3. Valid value (64-bit)
Follow the valid values of the four parameters:
• Displacement-An 8-bit, 16-bit, or 32-bit value.
• Base-The value in a 64-bit general-purpose register.
• Index-The value in a 64-bit general-purpose register.
• Scale factor-A value of 2, 4, or 8 that is multiplied by the index value.
4. Mod R/M (32-bit addressing)
We will use the Mod R/M byte later, so we will paste the 32-bit addressing format here:
Note in the above table, 1st of which will be used in our example, so pay attention to the following:
5. SIB (32-bit addressing)
Similarly, because Mod R/M bytes are used, the SIB bytes may also be used:
6. Example
6.1 preparations
With the above foundation, let's take a look at the actual example.
The following code generates an mov assembly code:
void Assembler::movl(Address dst, Register src) { InstructionMark im(this); prefix(dst, src); emit_int8((unsigned char)0x89); emit_operand(src, dst);}
prefix(dst,src)It is processing prefix and REX prefix. We will not pay attention to it here.
emit_int8((unsigned char) 0x89)Therefore, the name is to generate a byte. What is the code for the byte content 0x89?
Not in a hurry.emit_operand(src,dst)This is a long piece of code. Let's take a look at it:
Void receiver: emit_operand (Register reg, Register base, Register index, Address: ScaleFactor scale, int disp, RelocationHolder const & rspec, int rip_relative_correction) {relocInfo :: relocType rtype = (relocInfo: relocType) rspec. type (); // Encode the registers as needed in the fields they are used in int regenc = encode (reg) <3; int indexenc = index-> is_valid ()? Encode (index) <3: 0; int baseenc = base-> is_valid ()? Encode (base): 0; if (base-> is_valid () {if (index-> is_valid () {assert (scale! = Address: no_scale, "inconsistent address"); // [base + index * scale + disp] if (disp = 0 & rtype = relocInfo :: none & base! = Rbp LP64_ONLY (& base! = R13 )) {// [base + index * scale] // [00 reg 100] [ss index base]/***************** * ********* key points: for more information, see *************************/assert (index! = Rsp, "illegal addressing mode"); emit_int8 (0x04 | regenc); emit_int8 (scale <6 | indexenc | baseenc);} else if (is8bit (disp) & rtype = relocInfo: none ){//...} else {// [base + index * scale + disp32] // [10 reg 100] [ss index base] disp32 assert (index! = Rsp, "illegal addressing mode"); emit_int8 (0x84 | regenc); emit_int8 (scale <6 | indexenc | baseenc); emit_data (disp, rspec, disp32_operand);} else if (base = rsp LP64_ONLY (| base = r12 )){//...} else {//...}} else {//...}}
The focus of the above Code has been marked. Here we will extract it andemit_int8((unsigned char) 0x89)Combined:
emit_int8((unsigned char) 0x89)emit_int8(0x04 | regenc);emit_int8(scale << 6 | indexenc | baseenc);
Finally, the following Assembly Code (64-bit machine) is generated ):
mov %eax,(%rcx,%rbx,1)
Okay, the question is:
How can this compilation be obtained?
6.2. computing process
Let's give the following values:
regenc = 0x0,scale << 6 | indexenc | baseenc = 25
Perform a simple operation to obtain the following information:
Emit_int8 (unsigned char) 0x89) // obtain 0x89emit_int8 (0x04 | regenc); // obtain 0x04emit_int8 (scale <6 | indexenc | baseenc ); // get 0x19
Three bytes are used together:
0x89 0x04 0x19
1. What does 0x89 correspond?
From the table above, we can see that the JVM is working in 64-Bit mode, so we need to work with REX. W to "START", but in our example, it happens to be 0.
What are the commands behind 89/r?
MOV r/m64, r64 // 64-bit, giving the value in the register to the register or memory address
2. What does 0x04 mean?
Now we need to use the above Mod R/M Table and SIB table.
Use the second byte 0x04 to check the Mod R/M table. The source operand is the register EAX, and the addressing type is [-] [-], meaning:
The [-] [-] nomenclature means a SIB follows the ModR/M byte.
3. What does 0x19 represent?
Continue to query the SIB table. The corresponding byte 0x19 is:
base = ECXscaled index = EBX
4. assembly code:
// 32-bit mov % eax, % (ecx, ebx, 1) // 64-bit mov % rax, % (rcx, rbx, 1)
7. Conclusion
This article briefly discusses:
How to generate an assembly code based on the opcode and addressing mode.
.
Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.