//
// Copyright (c) 2018, Red Hat, Inc. All rights reserved.
// Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License version 2 only, as
// published by the Free Software Foundation.
//
// This code is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// version 2 for more details (a copy is included in the LICENSE file that
// accompanied this code).
//
// You should have received a copy of the GNU General Public License version
// 2 along with this work; if not, write to the Free Software Foundation,
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
//
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
// or visit www.oracle.com if you need additional information or have any
// questions.
//
//

source_hpp %{
#include "gc/shenandoah/shenandoahBarrierSet.hpp"
#include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
%}

instruct compareAndSwapP_shenandoah(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahCompareAndSwapP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);

  format %{
    "cmpxchg_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval with temp $tmp, #@compareAndSwapP_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapN_shenandoah(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahCompareAndSwapN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);

  format %{
    "cmpxchgw_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval with temp $tmp, #@compareAndSwapN_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapPAcq_shenandoah(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahCompareAndSwapP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);

  format %{
    "cmpxchg_acq_shenandoah_oop $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval with temp $tmp, #@compareAndSwapPAcq_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapNAcq_shenandoah(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahCompareAndSwapN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);

  format %{
    "cmpxchgw_acq_shenandoah_narrow_oop $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval with temp $tmp, #@compareAndSwapNAcq_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeN_shenandoah(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahCompareAndExchangeN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);
  effect(TEMP_DEF res, TEMP tmp, KILL cr);

  format %{
    "cmpxchgw_shenandoah $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeN_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   true /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeP_shenandoah(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahCompareAndExchangeP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP_DEF res, TEMP tmp, KILL cr);
  format %{
    "cmpxchg_shenandoah $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval with temp $tmp, #@compareAndExchangeP_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   true /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapN_shenandoah(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahWeakCompareAndSwapN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);
  format %{
    "cmpxchgw_shenandoah $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@weakCompareAndSwapN_shenandoah"
    "mv $res, EQ\t# $res <-- (EQ ? 1 : 0)"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    // Weak is not current supported by ShenandoahBarrierSet::cmpxchg_oop
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeNAcq_shenandoah(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahCompareAndExchangeN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP_DEF res, TEMP tmp, KILL cr);
  format %{
    "cmpxchgw_acq_shenandoah $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeNAcq_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register);
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   true /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangePAcq_shenandoah(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahCompareAndExchangeP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP_DEF res, TEMP tmp, KILL cr);
  format %{
    "cmpxchg_acq_shenandoah $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangePAcq_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register);
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   true /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapP_shenandoah(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  match(Set res (ShenandoahWeakCompareAndSwapP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);
  format %{
    "cmpxchg_shenandoah $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@weakCompareAndSwapP_shenandoah"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::relaxed /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapNAcq_shenandoah(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval, iRegNNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahWeakCompareAndSwapN mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);
  format %{
    "cmpxchgw_acq_shenandoah $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@weakCompareAndSwapNAcq_shenandoah"
    "mv $res, EQ\t# $res <-- (EQ ? 1 : 0)"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    // Weak is not current supported by ShenandoahBarrierSet::cmpxchg_oop
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapPAcq_shenandoah(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval, iRegPNoSp tmp, rFlagsReg cr) %{
  predicate(needs_acquiring_load_reserved(n));
  match(Set res (ShenandoahWeakCompareAndSwapP mem (Binary oldval newval)));
  ins_cost(10 * DEFAULT_COST);

  effect(TEMP tmp, KILL cr);
  format %{
    "cmpxchg_acq_shenandoah $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@weakCompareAndSwapPAcq_shenandoah"
    "mv $res, EQ\t# $res <-- (EQ ? 1 : 0)"
  %}

  ins_encode %{
    Register tmp = $tmp$$Register;
    __ mv(tmp, $oldval$$Register); // Must not clobber oldval.
    // Weak is not current supported by ShenandoahBarrierSet::cmpxchg_oop
    ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, $mem$$Register, tmp, $newval$$Register,
                                                   Assembler::aq /* acquire */, Assembler::rl /* release */,
                                                   false /* is_cae */, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}
