1 /*
2 * Copyright (c) 2008, 2015, 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 "asm/macroAssembler.inline.hpp"
27 #include "runtime/os.hpp"
28
breakpoint(AsmCondition cond)29 void MacroAssembler::breakpoint(AsmCondition cond) {
30 if (cond == al) {
31 emit_int32(0xe7f001f0);
32 } else {
33 call(CAST_FROM_FN_PTR(address, os::breakpoint), relocInfo::runtime_call_type, cond);
34 }
35 }
36
37 // atomic_cas_bool
38 //
39 // Perform an atomic compare and exchange and return bool result
40 //
41 // inputs:
42 // oldval value to compare to
43 // newval value to store if *(base+offset) == oldval
44 // base base address of storage location
45 // offset offset added to base to form dest address
46 // output:
47 // Z flag is set in success
48
atomic_cas_bool(Register oldval,Register newval,Register base,int offset,Register tmpreg)49 void MacroAssembler::atomic_cas_bool(Register oldval, Register newval, Register base, int offset, Register tmpreg) {
50 if (VM_Version::supports_ldrex()) {
51 Register tmp_reg;
52 if (tmpreg == noreg) {
53 push(LR);
54 tmp_reg = LR;
55 } else {
56 tmp_reg = tmpreg;
57 }
58 assert_different_registers(tmp_reg, oldval, newval, base);
59 Label loop;
60 bind(loop);
61 ldrex(tmp_reg, Address(base, offset));
62 subs(tmp_reg, tmp_reg, oldval);
63 strex(tmp_reg, newval, Address(base, offset), eq);
64 cmp(tmp_reg, 1, eq);
65 b(loop, eq);
66 cmp(tmp_reg, 0);
67 if (tmpreg == noreg) {
68 pop(tmp_reg);
69 }
70 } else if (VM_Version::supports_kuser_cmpxchg32()) {
71 // On armv5 platforms we must use the Linux kernel helper
72 // function for atomic cas operations since ldrex/strex is
73 // not supported.
74 //
75 // This is a special routine at a fixed address 0xffff0fc0 with
76 // with these arguments and results
77 //
78 // input:
79 // r0 = oldval, r1 = newval, r2 = ptr, lr = return adress
80 // output:
81 // r0 = 0 carry set on success
82 // r0 != 0 carry clear on failure
83 //
84 // r3, ip and flags are clobbered
85 //
86
87 Label loop;
88
89 push(RegisterSet(R0, R3) | RegisterSet(R12) | RegisterSet(LR));
90
91 Register tmp_reg = LR; // ignore the argument
92
93 assert_different_registers(tmp_reg, oldval, newval, base);
94
95 // Shuffle registers for kernel call
96 if (oldval != R0) {
97 if (newval == R0) {
98 mov(tmp_reg, newval);
99 newval = tmp_reg;
100 }
101 if (base == R0) {
102 mov(tmp_reg, base);
103 base = tmp_reg;
104 }
105 mov(R0, oldval);
106 }
107 if(newval != R1) {
108 if(base == R1) {
109 if(newval == R2) {
110 mov(tmp_reg, base);
111 base = tmp_reg;
112 }
113 else {
114 mov(R2, base);
115 base = R2;
116 }
117 }
118 mov(R1, newval);
119 }
120 if (base != R2)
121 mov(R2, base);
122
123 if (offset != 0)
124 add(R2, R2, offset);
125
126 mvn(R3, 0xf000);
127 mov(LR, PC);
128 sub(PC, R3, 0x3f);
129 cmp (R0, 0);
130
131 pop(RegisterSet(R0, R3) | RegisterSet(R12) | RegisterSet(LR));
132 } else {
133 // Should never run on a platform so old that it does not have kernel helper
134 stop("Atomic cmpxchg32 unsupported on this platform");
135 }
136 }
137
138 // atomic_cas
139 //
140 // Perform an atomic compare and exchange and return previous value
141 //
142 // inputs:
143 // prev temporary register (destroyed)
144 // oldval value to compare to
145 // newval value to store if *(base+offset) == oldval
146 // base base address of storage location
147 // offset offset added to base to form dest address
148 // output:
149 // returns previous value from *(base+offset) in R0
150
atomic_cas(Register temp1,Register temp2,Register oldval,Register newval,Register base,int offset)151 void MacroAssembler::atomic_cas(Register temp1, Register temp2, Register oldval, Register newval, Register base, int offset) {
152 if (temp1 != R0) {
153 // try to read the previous value directly in R0
154 if (temp2 == R0) {
155 // R0 declared free
156 temp2 = temp1;
157 temp1 = R0;
158 } else if ((oldval != R0) && (newval != R0) && (base != R0)) {
159 // free, and scratched on return
160 temp1 = R0;
161 }
162 }
163 if (VM_Version::supports_ldrex()) {
164 Label loop;
165 assert_different_registers(temp1, temp2, oldval, newval, base);
166
167 bind(loop);
168 ldrex(temp1, Address(base, offset));
169 cmp(temp1, oldval);
170 strex(temp2, newval, Address(base, offset), eq);
171 cmp(temp2, 1, eq);
172 b(loop, eq);
173 if (temp1 != R0) {
174 mov(R0, temp1);
175 }
176 } else if (VM_Version::supports_kuser_cmpxchg32()) {
177 // On armv5 platforms we must use the Linux kernel helper
178 // function for atomic cas operations since ldrex/strex is
179 // not supported.
180 //
181 // This is a special routine at a fixed address 0xffff0fc0
182 //
183 // input:
184 // r0 = oldval, r1 = newval, r2 = ptr, lr = return adress
185 // output:
186 // r0 = 0 carry set on success
187 // r0 != 0 carry clear on failure
188 //
189 // r3, ip and flags are clobbered
190 //
191 Label done;
192 Label loop;
193
194 push(RegisterSet(R1, R4) | RegisterSet(R12) | RegisterSet(LR));
195
196 if ( oldval != R0 || newval != R1 || base != R2 ) {
197 push(oldval);
198 push(newval);
199 push(base);
200 pop(R2);
201 pop(R1);
202 pop(R0);
203 }
204
205 if (offset != 0) {
206 add(R2, R2, offset);
207 }
208
209 mov(R4, R0);
210 bind(loop);
211 ldr(R0, Address(R2));
212 cmp(R0, R4);
213 b(done, ne);
214 mvn(R12, 0xf000);
215 mov(LR, PC);
216 sub(PC, R12, 0x3f);
217 b(loop, cc);
218 mov(R0, R4);
219 bind(done);
220
221 pop(RegisterSet(R1, R4) | RegisterSet(R12) | RegisterSet(LR));
222 } else {
223 // Should never run on a platform so old that it does not have kernel helper
224 stop("Atomic cmpxchg32 unsupported on this platform");
225 }
226 }
227
228 // atomic_cas64
229 //
230 // Perform a 64 bit atomic compare and exchange and return previous value
231 // as well as returning status in 'result' register
232 //
233 // inputs:
234 // oldval_lo, oldval_hi value to compare to
235 // newval_lo, newval_hi value to store if *(base+offset) == oldval
236 // base base address of storage location
237 // offset offset added to base to form dest address
238 // output:
239 // memval_lo, memval_hi, result
240 // returns previous value from *(base+offset) in memval_lo/hi
241 // returns status in result, 1==success, 0==failure
242 // C1 just uses status result
243 // VM code uses previous value returned in memval_lo/hi
244
atomic_cas64(Register memval_lo,Register memval_hi,Register result,Register oldval_lo,Register oldval_hi,Register newval_lo,Register newval_hi,Register base,int offset)245 void MacroAssembler::atomic_cas64(Register memval_lo, Register memval_hi, Register result, Register oldval_lo, Register oldval_hi, Register newval_lo, Register newval_hi, Register base, int offset) {
246 if (VM_Version::supports_ldrexd()) {
247 Label loop;
248 assert_different_registers(memval_lo, memval_hi, result, oldval_lo,
249 oldval_hi, newval_lo, newval_hi, base);
250 assert(memval_hi == memval_lo + 1 && memval_lo < R9, "cmpxchg_long: illegal registers");
251 assert(oldval_hi == oldval_lo + 1 && oldval_lo < R9, "cmpxchg_long: illegal registers");
252 assert(newval_hi == newval_lo + 1 && newval_lo < R9, "cmpxchg_long: illegal registers");
253 assert(result != R10, "cmpxchg_long: illegal registers");
254 assert(base != R10, "cmpxchg_long: illegal registers");
255
256 mov(result, 0);
257 bind(loop);
258 ldrexd(memval_lo, Address(base, offset));
259 cmp(memval_lo, oldval_lo);
260 cmp(memval_hi, oldval_hi, eq);
261 strexd(result, newval_lo, Address(base, offset), eq);
262 rsbs(result, result, 1, eq);
263 b(loop, eq);
264 } else if (VM_Version::supports_kuser_cmpxchg64()) {
265 // On armv5 platforms we must use the Linux kernel helper
266 // function for atomic cas64 operations since ldrexd/strexd is
267 // not supported.
268 //
269 // This is a special routine at a fixed address 0xffff0f60
270 //
271 // input:
272 // r0 = (long long *)oldval, r1 = (long long *)newval,
273 // r2 = ptr, lr = return adress
274 // output:
275 // r0 = 0 carry set on success
276 // r0 != 0 carry clear on failure
277 //
278 // r3, and flags are clobbered
279 //
280 Label done;
281 Label loop;
282
283 if (result != R12) {
284 push(R12);
285 }
286 push(RegisterSet(R10) | RegisterSet(LR));
287 mov(R10, SP); // Save SP
288
289 bic(SP, SP, StackAlignmentInBytes - 1); // align stack
290 push(RegisterSet(oldval_lo, oldval_hi));
291 push(RegisterSet(newval_lo, newval_hi));
292
293 if ((offset != 0) || (base != R12)) {
294 add(R12, base, offset);
295 }
296 push(RegisterSet(R0, R3));
297 bind(loop);
298 ldrd(memval_lo, Address(R12)); //current
299 ldrd(oldval_lo, Address(SP, 24));
300 cmp(memval_lo, oldval_lo);
301 cmp(memval_hi, oldval_hi, eq);
302 pop(RegisterSet(R0, R3), ne);
303 mov(result, 0, ne);
304 b(done, ne);
305 // Setup for kernel call
306 mov(R2, R12);
307 add(R0, SP, 24); // R0 == &oldval_lo
308 add(R1, SP, 16); // R1 == &newval_lo
309 mvn(R3, 0xf000); // call kernel helper at 0xffff0f60
310 mov(LR, PC);
311 sub(PC, R3, 0x9f);
312 b(loop, cc); // if Carry clear then oldval != current
313 // try again. Otherwise, return oldval
314 // Here on success
315 pop(RegisterSet(R0, R3));
316 mov(result, 1);
317 ldrd(memval_lo, Address(SP, 8));
318 bind(done);
319 pop(RegisterSet(newval_lo, newval_hi));
320 pop(RegisterSet(oldval_lo, oldval_hi));
321 mov(SP, R10); // restore SP
322 pop(RegisterSet(R10) | RegisterSet(LR));
323 if (result != R12) {
324 pop(R12);
325 }
326 } else {
327 stop("Atomic cmpxchg64 unsupported on this platform");
328 }
329 }
330