42664
|
1 |
/*
|
|
2 |
* Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
|
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation.
|
|
8 |
*
|
|
9 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
13 |
* accompanied this code).
|
|
14 |
*
|
|
15 |
* You should have received a copy of the GNU General Public License version
|
|
16 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 |
*
|
|
19 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
20 |
* or visit www.oracle.com if you need additional information or have any
|
|
21 |
* questions.
|
|
22 |
*
|
|
23 |
*/
|
|
24 |
|
|
25 |
#include "precompiled.hpp"
|
|
26 |
#include "assembler_arm.inline.hpp"
|
|
27 |
#include "code/codeCache.hpp"
|
|
28 |
#include "memory/resourceArea.hpp"
|
|
29 |
#include "nativeInst_arm.hpp"
|
|
30 |
#include "oops/oop.inline.hpp"
|
|
31 |
#include "runtime/handles.hpp"
|
|
32 |
#include "runtime/sharedRuntime.hpp"
|
|
33 |
#include "runtime/stubRoutines.hpp"
|
|
34 |
#include "utilities/ostream.hpp"
|
|
35 |
#ifdef COMPILER1
|
|
36 |
#include "c1/c1_Runtime1.hpp"
|
|
37 |
#endif
|
|
38 |
#include "code/icBuffer.hpp"
|
|
39 |
|
|
40 |
int NativeMovRegMem::offset() const {
|
|
41 |
switch (kind()) {
|
|
42 |
case instr_ldr_str:
|
|
43 |
return encoding() & 0xfff;
|
|
44 |
case instr_ldrh_strh:
|
|
45 |
return (encoding() & 0x0f) | ((encoding() >> 4) & 0xf0);
|
|
46 |
case instr_fld_fst:
|
|
47 |
return (encoding() & 0xff) << 2;
|
|
48 |
default:
|
|
49 |
ShouldNotReachHere();
|
|
50 |
return 0;
|
|
51 |
}
|
|
52 |
}
|
|
53 |
|
|
54 |
void NativeMovRegMem::set_offset(int x) {
|
|
55 |
assert(x >= 0 && x < 65536, "encoding constraint");
|
|
56 |
const int Rt = Rtemp->encoding();
|
|
57 |
|
|
58 |
// If offset is too large to be placed into single ldr/str instruction, we replace
|
|
59 |
// ldr Rd, [Rn, #offset]
|
|
60 |
// nop
|
|
61 |
// with
|
|
62 |
// add Rtemp, Rn, #offset_hi
|
|
63 |
// ldr Rd, [Rtemp, #offset_lo]
|
|
64 |
switch (kind()) {
|
|
65 |
case instr_ldr_str:
|
|
66 |
if (x < 4096) {
|
|
67 |
set_encoding((encoding() & 0xfffff000) | x);
|
|
68 |
} else {
|
|
69 |
NativeInstruction* next = nativeInstruction_at(next_raw_instruction_address());
|
|
70 |
assert(next->is_nop(), "must be");
|
|
71 |
next->set_encoding((encoding() & 0xfff0f000) | Rt << 16 | (x & 0xfff));
|
|
72 |
this->set_encoding((encoding() & 0x000f0000) | Rt << 12 | x >> 12 | 0xe2800a00);
|
|
73 |
}
|
|
74 |
break;
|
|
75 |
case instr_ldrh_strh:
|
|
76 |
if (x < 256) {
|
|
77 |
set_encoding((encoding() & 0xfffff0f0) | (x & 0x0f) | (x & 0xf0) << 4);
|
|
78 |
} else {
|
|
79 |
NativeInstruction* next = nativeInstruction_at(next_raw_instruction_address());
|
|
80 |
assert(next->is_nop(), "must be");
|
|
81 |
next->set_encoding((encoding() & 0xfff0f0f0) | Rt << 16 | (x & 0x0f) | (x & 0xf0) << 4);
|
|
82 |
this->set_encoding((encoding() & 0x000f0000) | Rt << 12 | x >> 8 | 0xe2800c00);
|
|
83 |
}
|
|
84 |
break;
|
|
85 |
case instr_fld_fst:
|
|
86 |
if (x < 1024) {
|
|
87 |
set_encoding((encoding() & 0xffffff00) | (x >> 2));
|
|
88 |
} else {
|
|
89 |
NativeInstruction* next = nativeInstruction_at(next_raw_instruction_address());
|
|
90 |
assert(next->is_nop(), "must be");
|
|
91 |
next->set_encoding((encoding() & 0xfff0ff00) | Rt << 16 | ((x >> 2) & 0xff));
|
|
92 |
this->set_encoding((encoding() & 0x000f0000) | Rt << 12 | x >> 10 | 0xe2800b00);
|
|
93 |
}
|
|
94 |
break;
|
|
95 |
default:
|
|
96 |
ShouldNotReachHere();
|
|
97 |
}
|
|
98 |
}
|
|
99 |
|
|
100 |
intptr_t NativeMovConstReg::data() const {
|
|
101 |
RawNativeInstruction* next = next_raw();
|
|
102 |
if (is_movw()) {
|
|
103 |
// Oop embedded in movw/movt instructions
|
|
104 |
assert(VM_Version::supports_movw(), "must be");
|
|
105 |
return (this->encoding() & 0x00000fff) | (this->encoding() & 0x000f0000) >> 4 |
|
|
106 |
(next->encoding() & 0x00000fff) << 16 | (next->encoding() & 0x000f0000) << 12;
|
|
107 |
} else {
|
|
108 |
// Oop is loaded from oops section or inlined in the code
|
|
109 |
int oop_offset;
|
|
110 |
if (is_ldr_literal()) {
|
|
111 |
// ldr Rd, [PC, #offset]
|
|
112 |
oop_offset = ldr_offset();
|
|
113 |
} else {
|
|
114 |
assert(next->is_ldr(), "must be");
|
|
115 |
oop_offset = (this->encoding() & 0xff) << 12 | (next->encoding() & 0xfff);
|
|
116 |
if (is_add_pc()) {
|
|
117 |
// add Rd, PC, #offset_hi
|
|
118 |
// ldr Rd, [Rd, #offset_lo]
|
|
119 |
assert(next->encoding() & (1 << 23), "sign mismatch");
|
|
120 |
// offset OK (both positive)
|
|
121 |
} else {
|
|
122 |
assert(is_sub_pc(), "must be");
|
|
123 |
// sub Rd, PC, #offset_hi
|
|
124 |
// ldr Rd, [Rd, -#offset_lo]
|
|
125 |
assert(!(next->encoding() & (1 << 23)), "sign mismatch");
|
|
126 |
// negative offsets
|
|
127 |
oop_offset = -oop_offset;
|
|
128 |
}
|
|
129 |
}
|
|
130 |
return *(int*)(instruction_address() + 8 + oop_offset);
|
|
131 |
}
|
|
132 |
}
|
|
133 |
|
|
134 |
void NativeMovConstReg::set_data(intptr_t x, address pc) {
|
|
135 |
// Find and replace the oop corresponding to this instruction in oops section
|
|
136 |
RawNativeInstruction* next = next_raw();
|
|
137 |
oop* oop_addr = NULL;
|
|
138 |
Metadata** metadata_addr = NULL;
|
|
139 |
CodeBlob* cb = CodeCache::find_blob(instruction_address());
|
|
140 |
if (cb != NULL) {
|
|
141 |
nmethod* nm = cb->as_nmethod_or_null();
|
|
142 |
if (nm != NULL) {
|
|
143 |
RelocIterator iter(nm, instruction_address(), next->instruction_address());
|
|
144 |
while (iter.next()) {
|
|
145 |
if (iter.type() == relocInfo::oop_type) {
|
|
146 |
oop_addr = iter.oop_reloc()->oop_addr();
|
|
147 |
*oop_addr = cast_to_oop(x);
|
|
148 |
break;
|
|
149 |
} else if (iter.type() == relocInfo::metadata_type) {
|
|
150 |
metadata_addr = iter.metadata_reloc()->metadata_addr();
|
|
151 |
*metadata_addr = (Metadata*)x;
|
|
152 |
break;
|
|
153 |
}
|
|
154 |
}
|
|
155 |
}
|
|
156 |
}
|
|
157 |
|
|
158 |
if (is_movw()) {
|
|
159 |
// data embedded in movw/movt instructions
|
|
160 |
assert(VM_Version::supports_movw(), "must be");
|
|
161 |
unsigned int lo = (unsigned int)x;
|
|
162 |
unsigned int hi = (unsigned int)(x >> 16);
|
|
163 |
this->set_encoding((this->encoding() & 0xfff0f000) | (lo & 0xf000) << 4 | (lo & 0xfff));
|
|
164 |
next->set_encoding((next->encoding() & 0xfff0f000) | (hi & 0xf000) << 4 | (hi & 0xfff));
|
|
165 |
} else if (oop_addr == NULL & metadata_addr == NULL) {
|
|
166 |
// A static ldr_literal (without oop or metadata relocation)
|
|
167 |
assert(is_ldr_literal(), "must be");
|
|
168 |
int offset = ldr_offset();
|
|
169 |
oop_addr = (oop*)(instruction_address() + 8 + offset);
|
|
170 |
*oop_addr = cast_to_oop(x);
|
|
171 |
} else {
|
|
172 |
// data is loaded from oop or metadata section
|
|
173 |
int offset;
|
|
174 |
|
|
175 |
address addr = oop_addr != NULL ? (address)oop_addr : (address)metadata_addr;
|
|
176 |
|
|
177 |
if(pc == 0) {
|
|
178 |
offset = addr - instruction_address() - 8;
|
|
179 |
} else {
|
|
180 |
offset = addr - pc - 8;
|
|
181 |
}
|
|
182 |
|
|
183 |
int sign = (offset >= 0) ? (1 << 23) : 0;
|
|
184 |
int delta = (offset >= 0) ? offset : (-offset);
|
|
185 |
assert(delta < 0x100000, "within accessible range");
|
|
186 |
if (is_ldr_literal()) {
|
|
187 |
// fix the ldr with the real offset to the oop/metadata table
|
|
188 |
assert(next->is_nop(), "must be");
|
|
189 |
if (delta < 4096) {
|
|
190 |
// ldr Rd, [PC, #offset]
|
|
191 |
set_encoding((encoding() & 0xff7ff000) | delta | sign);
|
|
192 |
assert(ldr_offset() == offset, "check encoding");
|
|
193 |
} else {
|
|
194 |
int cc = encoding() & 0xf0000000;
|
|
195 |
int Rd = (encoding() >> 12) & 0xf;
|
|
196 |
int Rt = Rd;
|
|
197 |
assert(Rt != 0xf, "Illegal destination register"); // or fix by using Rtemp
|
|
198 |
// move the ldr, fixing delta_lo and the source register
|
|
199 |
next->set_encoding((encoding() & 0xff70f000) | (Rt << 16) | (delta & 0xfff) | sign);
|
|
200 |
assert(next->is_ldr(), "must be");
|
|
201 |
if (offset > 0) {
|
|
202 |
// add Rt, PC, #delta_hi
|
|
203 |
// ldr Rd, [Rt, #delta_lo]
|
|
204 |
this->set_encoding((Rt << 12) | (delta >> 12) | 0x028f0a00 | cc);
|
|
205 |
assert(is_add_pc(), "must be");
|
|
206 |
} else {
|
|
207 |
// sub Rt, PC, #delta_hi
|
|
208 |
// ldr Rd, [Rt, -#delta_lo]
|
|
209 |
this->set_encoding((Rt << 12) | (delta >> 12) | 0x024f0a00 | cc);
|
|
210 |
assert(is_sub_pc(), "must be");
|
|
211 |
}
|
|
212 |
}
|
|
213 |
} else {
|
|
214 |
assert(is_pc_rel(), "must be");
|
|
215 |
assert(next->is_ldr(), "must be");
|
|
216 |
if (offset > 0) {
|
|
217 |
// add Rt, PC, #delta_hi
|
|
218 |
this->set_encoding((this->encoding() & 0xf00ff000) | 0x02800a00 | (delta >> 12));
|
|
219 |
assert(is_add_pc(), "must be");
|
|
220 |
} else {
|
|
221 |
// sub Rt, PC, #delta_hi
|
|
222 |
this->set_encoding((this->encoding() & 0xf00ff000) | 0x02400a00 | (delta >> 12));
|
|
223 |
assert(is_sub_pc(), "must be");
|
|
224 |
}
|
|
225 |
// ldr Rd, Rt, #delta_lo (or -#delta_lo)
|
|
226 |
next->set_encoding((next->encoding() & 0xff7ff000) | (delta & 0xfff) | sign);
|
|
227 |
}
|
|
228 |
}
|
|
229 |
}
|
|
230 |
|
|
231 |
void NativeMovConstReg::set_pc_relative_offset(address addr, address pc) {
|
|
232 |
int offset;
|
|
233 |
if (pc == 0) {
|
|
234 |
offset = addr - instruction_address() - 8;
|
|
235 |
} else {
|
|
236 |
offset = addr - pc - 8;
|
|
237 |
}
|
|
238 |
|
|
239 |
RawNativeInstruction* next = next_raw();
|
|
240 |
|
|
241 |
int sign = (offset >= 0) ? (1 << 23) : 0;
|
|
242 |
int delta = (offset >= 0) ? offset : (-offset);
|
|
243 |
assert(delta < 0x100000, "within accessible range");
|
|
244 |
if (is_ldr_literal()) {
|
|
245 |
if (delta < 4096) {
|
|
246 |
// ldr Rd, [PC, #offset]
|
|
247 |
set_encoding((encoding() & 0xff7ff000) | delta | sign);
|
|
248 |
assert(ldr_offset() == offset, "check encoding");
|
|
249 |
} else {
|
|
250 |
assert(next->is_nop(), "must be");
|
|
251 |
int cc = encoding() & 0xf0000000;
|
|
252 |
int Rd = (encoding() >> 12) & 0xf;
|
|
253 |
int Rt = Rd;
|
|
254 |
assert(Rt != 0xf, "Illegal destination register"); // or fix by using Rtemp
|
|
255 |
// move the ldr, fixing delta_lo and the source register
|
|
256 |
next->set_encoding((encoding() & 0xff70f000) | (Rt << 16) | (delta & 0xfff) | sign);
|
|
257 |
assert(next->is_ldr(), "must be");
|
|
258 |
if (offset > 0) {
|
|
259 |
// add Rt, PC, #delta_hi
|
|
260 |
// ldr Rd, [Rt, #delta_lo]
|
|
261 |
this->set_encoding((Rt << 12) | (delta >> 12) | 0x028f0a00 | cc);
|
|
262 |
assert(is_add_pc(), "must be");
|
|
263 |
} else {
|
|
264 |
// sub Rt, PC, #delta_hi
|
|
265 |
// ldr Rd, [Rt, -#delta_lo]
|
|
266 |
this->set_encoding((Rt << 12) | (delta >> 12) | 0x024f0a00 | cc);
|
|
267 |
assert(is_sub_pc(), "must be");
|
|
268 |
}
|
|
269 |
}
|
|
270 |
} else {
|
|
271 |
assert(is_pc_rel(), "must be");
|
|
272 |
assert(next->is_ldr(), "must be");
|
|
273 |
if (offset > 0) {
|
|
274 |
// add Rt, PC, #delta_hi
|
|
275 |
this->set_encoding((this->encoding() & 0xf00ff000) | 0x02800a00 | (delta >> 12));
|
|
276 |
assert(is_add_pc(), "must be");
|
|
277 |
} else {
|
|
278 |
// sub Rt, PC, #delta_hi
|
|
279 |
this->set_encoding((this->encoding() & 0xf00ff000) | 0x02400a00 | (delta >> 12));
|
|
280 |
assert(is_sub_pc(), "must be");
|
|
281 |
}
|
|
282 |
// ldr Rd, Rt, #delta_lo (or -#delta_lo)
|
|
283 |
next->set_encoding((next->encoding() & 0xff7ff000) | (delta & 0xfff) | sign);
|
|
284 |
}
|
|
285 |
}
|
|
286 |
|
|
287 |
void RawNativeJump::check_verified_entry_alignment(address entry, address verified_entry) {
|
|
288 |
}
|
|
289 |
|
|
290 |
void RawNativeJump::patch_verified_entry(address entry, address verified_entry, address dest) {
|
|
291 |
assert(dest == SharedRuntime::get_handle_wrong_method_stub(), "should be");
|
|
292 |
int *a = (int *)verified_entry;
|
|
293 |
a[0] = zombie_illegal_instruction; // always illegal
|
|
294 |
ICache::invalidate_range((address)&a[0], sizeof a[0]);
|
|
295 |
}
|
|
296 |
|
|
297 |
void NativeGeneralJump::insert_unconditional(address code_pos, address entry) {
|
|
298 |
int offset = (int)(entry - code_pos - 8);
|
|
299 |
assert(offset < 0x2000000 && offset > -0x2000000, "encoding constraint");
|
|
300 |
nativeInstruction_at(code_pos)->set_encoding(0xea000000 | ((unsigned int)offset << 6 >> 8));
|
|
301 |
}
|
|
302 |
|
|
303 |
static address raw_call_for(address return_address) {
|
|
304 |
CodeBlob* cb = CodeCache::find_blob(return_address);
|
|
305 |
nmethod* nm = cb->as_nmethod_or_null();
|
|
306 |
if (nm == NULL) {
|
|
307 |
ShouldNotReachHere();
|
|
308 |
return NULL;
|
|
309 |
}
|
|
310 |
// Look back 4 instructions, to allow for ic_call
|
|
311 |
address begin = MAX2(return_address - 4*NativeInstruction::instruction_size, nm->code_begin());
|
|
312 |
RelocIterator iter(nm, begin, return_address);
|
|
313 |
while (iter.next()) {
|
|
314 |
Relocation* reloc = iter.reloc();
|
|
315 |
if (reloc->is_call()) {
|
|
316 |
address call = reloc->addr();
|
|
317 |
if (nativeInstruction_at(call)->is_call()) {
|
|
318 |
if (nativeCall_at(call)->return_address() == return_address) {
|
|
319 |
return call;
|
|
320 |
}
|
|
321 |
} else {
|
|
322 |
// Some "calls" are really jumps
|
|
323 |
assert(nativeInstruction_at(call)->is_jump(), "must be call or jump");
|
|
324 |
}
|
|
325 |
}
|
|
326 |
}
|
|
327 |
return NULL;
|
|
328 |
}
|
|
329 |
|
|
330 |
bool RawNativeCall::is_call_before(address return_address) {
|
|
331 |
return (raw_call_for(return_address) != NULL);
|
|
332 |
}
|
|
333 |
|
|
334 |
NativeCall* rawNativeCall_before(address return_address) {
|
|
335 |
address call = raw_call_for(return_address);
|
|
336 |
assert(call != NULL, "must be");
|
|
337 |
return nativeCall_at(call);
|
|
338 |
}
|
|
339 |
|