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 
  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 
  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 
 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 
 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 }