1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
%def op_invoke_custom():
EXPORT_PC
FETCH r0, 1 // call_site index, first argument of runtime call.
b NterpCommonInvokeCustom
%def op_invoke_custom_range():
EXPORT_PC
FETCH r0, 1 // call_site index, first argument of runtime call.
b NterpCommonInvokeCustomRange
%def invoke_direct_or_super(helper="", range="", is_super=""):
EXPORT_PC
// Fast-path which gets the method from thread-local cache.
FETCH_FROM_THREAD_CACHE r0, 2f
1:
// Load the first argument (the 'this' pointer).
FETCH r1, 2
.if !$range
and r1, r1, #0xf
.endif
GET_VREG r1, r1
cmp r1, #0
beq common_errNullObject // bail if null
b $helper
2:
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
bl nterp_get_method
.if $is_super
b 1b
.else
tst r0, #1
beq 1b
and r0, r0, #-2 // Remove the extra bit that marks it's a String.<init> method.
.if $range
b NterpHandleStringInitRange
.else
b NterpHandleStringInit
.endif
.endif
%def op_invoke_direct():
% invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0", is_super="0")
%def op_invoke_direct_range():
% invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1", is_super="0")
%def op_invoke_super():
% invoke_direct_or_super(helper="NterpCommonInvokeInstance", range="0", is_super="1")
%def op_invoke_super_range():
% invoke_direct_or_super(helper="NterpCommonInvokeInstanceRange", range="1", is_super="1")
%def op_invoke_polymorphic():
EXPORT_PC
// No need to fetch the target method.
// Load the first argument (the 'this' pointer).
FETCH r1, 2
and r1, r1, #0xf
GET_VREG r1, r1
cmp r1, #0
beq common_errNullObject // bail if null
b NterpCommonInvokePolymorphic
%def op_invoke_polymorphic_range():
EXPORT_PC
// No need to fetch the target method.
// Load the first argument (the 'this' pointer).
FETCH r1, 2
GET_VREG r1, r1
cmp r1, #0
beq common_errNullObject // bail if null
b NterpCommonInvokePolymorphicRange
%def invoke_interface(range=""):
% slow_path = add_helper(lambda: op_invoke_interface_slow_path())
EXPORT_PC
// Fast-path which gets the method from thread-local cache.
FETCH_FROM_THREAD_CACHE r4, ${slow_path}
.L${opcode}_resume:
// First argument is the 'this' pointer.
FETCH r1, 2
.if !$range
and r1, r1, #0xf
.endif
GET_VREG r1, r1
// Note: if r1 is null, this will be handled by our SIGSEGV handler.
ldr r2, [r1, #MIRROR_OBJECT_CLASS_OFFSET]
// Test the first two bits of the fetched ArtMethod:
// - If the first bit is set, this is a method on j.l.Object
// - If the second bit is set, this is a default method.
tst r4, #3
bne 2f
ldrh r3, [r4, #ART_METHOD_IMT_INDEX_OFFSET]
1:
ldr r2, [r2, #MIRROR_CLASS_IMT_PTR_OFFSET_32]
ldr r0, [r2, r3, lsl #2]
.if $range
b NterpCommonInvokeInterfaceRange
.else
b NterpCommonInvokeInterface
.endif
2:
tst r4, #1
bne 3f
and r4, r4, #-4
ldrh r3, [r4, #ART_METHOD_METHOD_INDEX_OFFSET]
and r3, r3, #ART_METHOD_IMT_MASK
b 1b
3:
lsr r4, r4, #16
add r2, r2, #MIRROR_CLASS_VTABLE_OFFSET_32
ldr r0, [r2, r4, lsl #2]
.if $range
b NterpCommonInvokeInstanceRange
.else
b NterpCommonInvokeInstance
.endif
%def op_invoke_interface_slow_path():
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
bl nterp_get_method
mov r4, r0
b .L${opcode}_resume
%def op_invoke_interface():
% invoke_interface(range="0")
%def op_invoke_interface_range():
% invoke_interface(range="1")
%def invoke_static(helper=""):
EXPORT_PC
// Fast-path which gets the method from thread-local cache.
FETCH_FROM_THREAD_CACHE r0, 1f
b $helper
1:
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
bl nterp_get_method
b $helper
%def op_invoke_static():
% invoke_static(helper="NterpCommonInvokeStatic")
%def op_invoke_static_range():
% invoke_static(helper="NterpCommonInvokeStaticRange")
%def invoke_virtual(helper="", range=""):
EXPORT_PC
// Fast-path which gets the vtable offset from thread-local cache.
FETCH_FROM_THREAD_CACHE r2, 2f
1:
FETCH r1, 2
.if !$range
and r1, r1, #0xf
.endif
GET_VREG r1, r1
// Note: if r1 is null, this will be handled by our SIGSEGV handler.
ldr r0, [r1, #MIRROR_OBJECT_CLASS_OFFSET]
add r0, r0, #MIRROR_CLASS_VTABLE_OFFSET_32
ldr r0, [r0, r2, lsl #2]
b $helper
2:
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
bl nterp_get_method
mov r2, r0
b 1b
%def op_invoke_virtual():
% invoke_virtual(helper="NterpCommonInvokeInstance", range="0")
%def op_invoke_virtual_range():
% invoke_virtual(helper="NterpCommonInvokeInstanceRange", range="1")
|