invokevirtual class.method()
in bytecode. The class here is the declared type of the reference and not the actual type of the reference. The method to be called depends on the actual type of the reference which can only be resolved at run time.
Jato Internals for a virtual call
The vm maintains a vm_class structure corresponding to each class. Each vm_class structure contains a vtable which is an array of pointers to executable code of methods in that class. The vtable is organized such that the index (into the vtable) of a virtual method is the same in the base class and all derived classes. i.e if name() has index 10 in Fruit class. Its index is 10 in Apple, Orange, or any other class derived from Fruit. This index is maintained in the virtual_index field in vm_method structure. So executing a virtual call involves.
- Loading the correct vtable entry using the virtual_index.
- Executing a call instruction to that address.
/* object reference */
call_target = state->left->reg1;
/* object class */
select_insn(s, tree, membase_reg_insn(INSN_MOV_MEMBASE_REG, call_target, offsetof(struct vm_object, class), call_target));
/* vtable */
select_insn(s, tree, membase_reg_insn(INSN_MOV_MEMBASE_REG, call_target, offsetof(struct vm_class, vtable), call_target));
/* native ptr */
select_insn(s, tree, imm_reg_insn(INSN_ADD_IMM_REG, method_offset, call_target));
/* invoke method */
call_insn = reverse_reg_insn(INSN_CALL_REG, call_target);
select_safepoint_insn(s, tree, call_insn);
Initially this call goes to the trampoline function jit_magic_trampoline(). It compiles the method body and patches the vtable entry to point directly to the compiled code. This vtable patching can be found in jit/vtable.c:fixup_vtable() [See also emit_trampoline()].
We can trace the output of different phases of Jato by using the option -Xtrace:jit. Below we show the output of different phases for the virtual call.
Fruit f;
.
.
f.name();
HIR
[main] INVOKEVIRTUAL:
[main] target_method: [0xa24d9d0 'jvm/MethodInvokeVirtualTest$Fruit.name()Ljava/lang/String;' (12)]
[main] args_list:
[main] ARG_THIS:
[main] arg_expression: [temporary reference 0xa415a60 (low)]
[main] result: [temporary reference 0xa4172e8 (low)]
The HIR is very "close" to java bytecode. The only information available at this point is a method descriptor.
LIR
[main] [ 1 ] 2: push_reg r11 ; Push this to stack
[main] [ 1 ] 4: mov_membase_reg $0x0(r11), r11 ; r11 <- this->class
[main] [ 1 ] 6: mov_membase_reg $0x7c(r11), r11 ; r11 <- this->class->vtable
[main] [ 1 ] 8: add_imm_reg $0x30, r11 ; r11 <- &this->class->vtable[method_index]
[main] [ 1 ] 10: test_imm_memdisp $0x0, ($0xa0ae000)
[main] [ 1 ] 12: call_reg (r11) ; jump to the virtual method
The LIR is very close to machine code. The actual machine registers used are determined only during code generation. The LIR encodes the logic to lookup the vtable entry and jump to it. The number $0x30 depends on the virtual method to be called (the virtual_index).
Machine Code
[main] [ 1 ] 0xa775226c: 57 push %edi
[main] [ 1 ] 0xa775226d: 8b 3f mov (%edi),%edi
[main] [ 1 ] 0xa775226f: 8b 7f 7c mov 0x7c(%edi),%edi
[main] [ 1 ] 0xa7752272: 81 c7 30 00 00 00 add $0x30,%edi
[main] [ 1 ] 0xa7752278: f6 04 25 00 e0 0a 0a 00 testb $0x0,0xa0ae000(,%eiz,1)
[main] [ 1 ] 0xa7752280: ff 17 call *(%edi)
This code looks very similar to LIR except that the actual machine registers to be used have been determined.
No comments:
Post a Comment