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