Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek riscv.ad   Sprache: unbekannt

 
Spracherkennung für: .ad vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

//
// Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
// Copyright (c) 2020, 2022, 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.
//
//

// RISCV Architecture Description File

//----------REGISTER DEFINITION BLOCK------------------------------------------
// This information is used by the matcher and the register allocator to
// describe individual registers and classes of registers within the target
// architecture.

register %{
//----------Architecture Description Register Definitions----------------------
// General Registers
// "reg_def"  name ( register save type, C convention save type,
//                   ideal register type, encoding );
// Register Save Types:
//
// NS  = No-Save:       The register allocator assumes that these registers
//                      can be used without saving upon entry to the method, &
//                      that they do not need to be saved at call sites.
//
// SOC = Save-On-Call:  The register allocator assumes that these registers
//                      can be used without saving upon entry to the method,
//                      but that they must be saved at call sites.
//
// SOE = Save-On-Entry: The register allocator assumes that these registers
//                      must be saved before using them upon entry to the
//                      method, but they do not need to be saved at call
//                      sites.
//
// AS  = Always-Save:   The register allocator assumes that these registers
//                      must be saved before using them upon entry to the
//                      method, & that they must be saved at call sites.
//
// Ideal Register Type is used to determine how to save & restore a
// register.  Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get
// spilled with LoadP/StoreP.  If the register supports both, use Op_RegI.
//
// The encoding number is the actual bit-pattern placed into the opcodes.

// We must define the 64 bit int registers in two 32 bit halves, the
// real lower register and a virtual upper half register. upper halves
// are used by the register allocator but are not actually supplied as
// operands to memory ops.
//
// follow the C1 compiler in making registers
//
//   x7, x9-x17, x27-x31 volatile (caller save)
//   x0-x4, x8, x23 system (no save, no allocate)
//   x5-x6 non-allocatable (so we can use them as temporary regs)

//
// as regards Java usage. we don't use any callee save registers
// because this makes it difficult to de-optimise a frame (see comment
// in x86 implementation of Deoptimization::unwind_callee_save_values)
//

// General Registers

reg_def R0      ( NS,  NS,  Op_RegI, 0,  x0->as_VMReg()         ); // zr
reg_def R0_H    ( NS,  NS,  Op_RegI, 0,  x0->as_VMReg()->next() );
reg_def R1      ( NS,  SOC, Op_RegI, 1,  x1->as_VMReg()         ); // ra
reg_def R1_H    ( NS,  SOC, Op_RegI, 1,  x1->as_VMReg()->next() );
reg_def R2      ( NS,  SOE, Op_RegI, 2,  x2->as_VMReg()         ); // sp
reg_def R2_H    ( NS,  SOE, Op_RegI, 2,  x2->as_VMReg()->next() );
reg_def R3      ( NS,  NS,  Op_RegI, 3,  x3->as_VMReg()         ); // gp
reg_def R3_H    ( NS,  NS,  Op_RegI, 3,  x3->as_VMReg()->next() );
reg_def R4      ( NS,  NS,  Op_RegI, 4,  x4->as_VMReg()         ); // tp
reg_def R4_H    ( NS,  NS,  Op_RegI, 4,  x4->as_VMReg()->next() );
reg_def R7      ( SOC, SOC, Op_RegI, 7,  x7->as_VMReg()         );
reg_def R7_H    ( SOC, SOC, Op_RegI, 7,  x7->as_VMReg()->next() );
reg_def R8      ( NS,  SOE, Op_RegI, 8,  x8->as_VMReg()         ); // fp
reg_def R8_H    ( NS,  SOE, Op_RegI, 8,  x8->as_VMReg()->next() );
reg_def R9      ( SOC, SOE, Op_RegI, 9,  x9->as_VMReg()         );
reg_def R9_H    ( SOC, SOE, Op_RegI, 9,  x9->as_VMReg()->next() );
reg_def R10     ( SOC, SOC, Op_RegI, 10, x10->as_VMReg()        );
reg_def R10_H   ( SOC, SOC, Op_RegI, 10, x10->as_VMReg()->next());
reg_def R11     ( SOC, SOC, Op_RegI, 11, x11->as_VMReg()        );
reg_def R11_H   ( SOC, SOC, Op_RegI, 11, x11->as_VMReg()->next());
reg_def R12     ( SOC, SOC, Op_RegI, 12, x12->as_VMReg()        );
reg_def R12_H   ( SOC, SOC, Op_RegI, 12, x12->as_VMReg()->next());
reg_def R13     ( SOC, SOC, Op_RegI, 13, x13->as_VMReg()        );
reg_def R13_H   ( SOC, SOC, Op_RegI, 13, x13->as_VMReg()->next());
reg_def R14     ( SOC, SOC, Op_RegI, 14, x14->as_VMReg()        );
reg_def R14_H   ( SOC, SOC, Op_RegI, 14, x14->as_VMReg()->next());
reg_def R15     ( SOC, SOC, Op_RegI, 15, x15->as_VMReg()        );
reg_def R15_H   ( SOC, SOC, Op_RegI, 15, x15->as_VMReg()->next());
reg_def R16     ( SOC, SOC, Op_RegI, 16, x16->as_VMReg()        );
reg_def R16_H   ( SOC, SOC, Op_RegI, 16, x16->as_VMReg()->next());
reg_def R17     ( SOC, SOC, Op_RegI, 17, x17->as_VMReg()        );
reg_def R17_H   ( SOC, SOC, Op_RegI, 17, x17->as_VMReg()->next());
reg_def R18     ( SOC, SOE, Op_RegI, 18, x18->as_VMReg()        );
reg_def R18_H   ( SOC, SOE, Op_RegI, 18, x18->as_VMReg()->next());
reg_def R19     ( SOC, SOE, Op_RegI, 19, x19->as_VMReg()        );
reg_def R19_H   ( SOC, SOE, Op_RegI, 19, x19->as_VMReg()->next());
reg_def R20     ( SOC, SOE, Op_RegI, 20, x20->as_VMReg()        ); // caller esp
reg_def R20_H   ( SOC, SOE, Op_RegI, 20, x20->as_VMReg()->next());
reg_def R21     ( SOC, SOE, Op_RegI, 21, x21->as_VMReg()        );
reg_def R21_H   ( SOC, SOE, Op_RegI, 21, x21->as_VMReg()->next());
reg_def R22     ( SOC, SOE, Op_RegI, 22, x22->as_VMReg()        );
reg_def R22_H   ( SOC, SOE, Op_RegI, 22, x22->as_VMReg()->next());
reg_def R23     ( NS,  SOE, Op_RegI, 23, x23->as_VMReg()        ); // java thread
reg_def R23_H   ( NS,  SOE, Op_RegI, 23, x23->as_VMReg()->next());
reg_def R24     ( SOC, SOE, Op_RegI, 24, x24->as_VMReg()        );
reg_def R24_H   ( SOC, SOE, Op_RegI, 24, x24->as_VMReg()->next());
reg_def R25     ( SOC, SOE, Op_RegI, 25, x25->as_VMReg()        );
reg_def R25_H   ( SOC, SOE, Op_RegI, 25, x25->as_VMReg()->next());
reg_def R26     ( SOC, SOE, Op_RegI, 26, x26->as_VMReg()        );
reg_def R26_H   ( SOC, SOE, Op_RegI, 26, x26->as_VMReg()->next());
reg_def R27     ( SOC, SOE, Op_RegI, 27, x27->as_VMReg()        ); // heapbase
reg_def R27_H   ( SOC, SOE, Op_RegI, 27, x27->as_VMReg()->next());
reg_def R28     ( SOC, SOC, Op_RegI, 28, x28->as_VMReg()        );
reg_def R28_H   ( SOC, SOC, Op_RegI, 28, x28->as_VMReg()->next());
reg_def R29     ( SOC, SOC, Op_RegI, 29, x29->as_VMReg()        );
reg_def R29_H   ( SOC, SOC, Op_RegI, 29, x29->as_VMReg()->next());
reg_def R30     ( SOC, SOC, Op_RegI, 30, x30->as_VMReg()        );
reg_def R30_H   ( SOC, SOC, Op_RegI, 30, x30->as_VMReg()->next());
reg_def R31     ( SOC, SOC, Op_RegI, 31, x31->as_VMReg()        );
reg_def R31_H   ( SOC, SOC, Op_RegI, 31, x31->as_VMReg()->next());

// ----------------------------
// Float/Double Registers
// ----------------------------

// Double Registers

// The rules of ADL require that double registers be defined in pairs.
// Each pair must be two 32-bit values, but not necessarily a pair of
// single float registers. In each pair, ADLC-assigned register numbers
// must be adjacent, with the lower number even. Finally, when the
// CPU stores such a register pair to memory, the word associated with
// the lower ADLC-assigned number must be stored to the lower address.

// RISCV has 32 floating-point registers. Each can store a single
// or double precision floating-point value.

// for Java use float registers f0-f31 are always save on call whereas
// the platform ABI treats f8-f9 and f18-f27 as callee save). Other
// float registers are SOC as per the platform spec

reg_def F0    ( SOC, SOC, Op_RegF,  0,  f0->as_VMReg()          );
reg_def F0_H  ( SOC, SOC, Op_RegF,  0,  f0->as_VMReg()->next()  );
reg_def F1    ( SOC, SOC, Op_RegF,  1,  f1->as_VMReg()          );
reg_def F1_H  ( SOC, SOC, Op_RegF,  1,  f1->as_VMReg()->next()  );
reg_def F2    ( SOC, SOC, Op_RegF,  2,  f2->as_VMReg()          );
reg_def F2_H  ( SOC, SOC, Op_RegF,  2,  f2->as_VMReg()->next()  );
reg_def F3    ( SOC, SOC, Op_RegF,  3,  f3->as_VMReg()          );
reg_def F3_H  ( SOC, SOC, Op_RegF,  3,  f3->as_VMReg()->next()  );
reg_def F4    ( SOC, SOC, Op_RegF,  4,  f4->as_VMReg()          );
reg_def F4_H  ( SOC, SOC, Op_RegF,  4,  f4->as_VMReg()->next()  );
reg_def F5    ( SOC, SOC, Op_RegF,  5,  f5->as_VMReg()          );
reg_def F5_H  ( SOC, SOC, Op_RegF,  5,  f5->as_VMReg()->next()  );
reg_def F6    ( SOC, SOC, Op_RegF,  6,  f6->as_VMReg()          );
reg_def F6_H  ( SOC, SOC, Op_RegF,  6,  f6->as_VMReg()->next()  );
reg_def F7    ( SOC, SOC, Op_RegF,  7,  f7->as_VMReg()          );
reg_def F7_H  ( SOC, SOC, Op_RegF,  7,  f7->as_VMReg()->next()  );
reg_def F8    ( SOC, SOE, Op_RegF,  8,  f8->as_VMReg()          );
reg_def F8_H  ( SOC, SOE, Op_RegF,  8,  f8->as_VMReg()->next()  );
reg_def F9    ( SOC, SOE, Op_RegF,  9,  f9->as_VMReg()          );
reg_def F9_H  ( SOC, SOE, Op_RegF,  9,  f9->as_VMReg()->next()  );
reg_def F10   ( SOC, SOC, Op_RegF,  10, f10->as_VMReg()         );
reg_def F10_H ( SOC, SOC, Op_RegF,  10, f10->as_VMReg()->next() );
reg_def F11   ( SOC, SOC, Op_RegF,  11, f11->as_VMReg()         );
reg_def F11_H ( SOC, SOC, Op_RegF,  11, f11->as_VMReg()->next() );
reg_def F12   ( SOC, SOC, Op_RegF,  12, f12->as_VMReg()         );
reg_def F12_H ( SOC, SOC, Op_RegF,  12, f12->as_VMReg()->next() );
reg_def F13   ( SOC, SOC, Op_RegF,  13, f13->as_VMReg()         );
reg_def F13_H ( SOC, SOC, Op_RegF,  13, f13->as_VMReg()->next() );
reg_def F14   ( SOC, SOC, Op_RegF,  14, f14->as_VMReg()         );
reg_def F14_H ( SOC, SOC, Op_RegF,  14, f14->as_VMReg()->next() );
reg_def F15   ( SOC, SOC, Op_RegF,  15, f15->as_VMReg()         );
reg_def F15_H ( SOC, SOC, Op_RegF,  15, f15->as_VMReg()->next() );
reg_def F16   ( SOC, SOC, Op_RegF,  16, f16->as_VMReg()         );
reg_def F16_H ( SOC, SOC, Op_RegF,  16, f16->as_VMReg()->next() );
reg_def F17   ( SOC, SOC, Op_RegF,  17, f17->as_VMReg()         );
reg_def F17_H ( SOC, SOC, Op_RegF,  17, f17->as_VMReg()->next() );
reg_def F18   ( SOC, SOE, Op_RegF,  18, f18->as_VMReg()         );
reg_def F18_H ( SOC, SOE, Op_RegF,  18, f18->as_VMReg()->next() );
reg_def F19   ( SOC, SOE, Op_RegF,  19, f19->as_VMReg()         );
reg_def F19_H ( SOC, SOE, Op_RegF,  19, f19->as_VMReg()->next() );
reg_def F20   ( SOC, SOE, Op_RegF,  20, f20->as_VMReg()         );
reg_def F20_H ( SOC, SOE, Op_RegF,  20, f20->as_VMReg()->next() );
reg_def F21   ( SOC, SOE, Op_RegF,  21, f21->as_VMReg()         );
reg_def F21_H ( SOC, SOE, Op_RegF,  21, f21->as_VMReg()->next() );
reg_def F22   ( SOC, SOE, Op_RegF,  22, f22->as_VMReg()         );
reg_def F22_H ( SOC, SOE, Op_RegF,  22, f22->as_VMReg()->next() );
reg_def F23   ( SOC, SOE, Op_RegF,  23, f23->as_VMReg()         );
reg_def F23_H ( SOC, SOE, Op_RegF,  23, f23->as_VMReg()->next() );
reg_def F24   ( SOC, SOE, Op_RegF,  24, f24->as_VMReg()         );
reg_def F24_H ( SOC, SOE, Op_RegF,  24, f24->as_VMReg()->next() );
reg_def F25   ( SOC, SOE, Op_RegF,  25, f25->as_VMReg()         );
reg_def F25_H ( SOC, SOE, Op_RegF,  25, f25->as_VMReg()->next() );
reg_def F26   ( SOC, SOE, Op_RegF,  26, f26->as_VMReg()         );
reg_def F26_H ( SOC, SOE, Op_RegF,  26, f26->as_VMReg()->next() );
reg_def F27   ( SOC, SOE, Op_RegF,  27, f27->as_VMReg()         );
reg_def F27_H ( SOC, SOE, Op_RegF,  27, f27->as_VMReg()->next() );
reg_def F28   ( SOC, SOC, Op_RegF,  28, f28->as_VMReg()         );
reg_def F28_H ( SOC, SOC, Op_RegF,  28, f28->as_VMReg()->next() );
reg_def F29   ( SOC, SOC, Op_RegF,  29, f29->as_VMReg()         );
reg_def F29_H ( SOC, SOC, Op_RegF,  29, f29->as_VMReg()->next() );
reg_def F30   ( SOC, SOC, Op_RegF,  30, f30->as_VMReg()         );
reg_def F30_H ( SOC, SOC, Op_RegF,  30, f30->as_VMReg()->next() );
reg_def F31   ( SOC, SOC, Op_RegF,  31, f31->as_VMReg()         );
reg_def F31_H ( SOC, SOC, Op_RegF,  31, f31->as_VMReg()->next() );

// ----------------------------
// Vector Registers
// ----------------------------

// For RVV vector registers, we simply extend vector register size to 4
// 'logical' slots. This is nominally 128 bits but it actually covers
// all possible 'physical' RVV vector register lengths from 128 ~ 1024
// bits. The 'physical' RVV vector register length is detected during
// startup, so the register allocator is able to identify the correct
// number of bytes needed for an RVV spill/unspill.

reg_def V0    ( SOC, SOC, Op_VecA, 0,  v0->as_VMReg()           );
reg_def V0_H  ( SOC, SOC, Op_VecA, 0,  v0->as_VMReg()->next()   );
reg_def V0_J  ( SOC, SOC, Op_VecA, 0,  v0->as_VMReg()->next(2)  );
reg_def V0_K  ( SOC, SOC, Op_VecA, 0,  v0->as_VMReg()->next(3)  );

reg_def V1    ( SOC, SOC, Op_VecA, 1,  v1->as_VMReg()          );
reg_def V1_H  ( SOC, SOC, Op_VecA, 1,  v1->as_VMReg()->next()   );
reg_def V1_J  ( SOC, SOC, Op_VecA, 1,  v1->as_VMReg()->next(2)  );
reg_def V1_K  ( SOC, SOC, Op_VecA, 1,  v1->as_VMReg()->next(3)  );

reg_def V2    ( SOC, SOC, Op_VecA, 2,  v2->as_VMReg()           );
reg_def V2_H  ( SOC, SOC, Op_VecA, 2,  v2->as_VMReg()->next()   );
reg_def V2_J  ( SOC, SOC, Op_VecA, 2,  v2->as_VMReg()->next(2)  );
reg_def V2_K  ( SOC, SOC, Op_VecA, 2,  v2->as_VMReg()->next(3)  );

reg_def V3    ( SOC, SOC, Op_VecA, 3,  v3->as_VMReg()           );
reg_def V3_H  ( SOC, SOC, Op_VecA, 3,  v3->as_VMReg()->next()   );
reg_def V3_J  ( SOC, SOC, Op_VecA, 3,  v3->as_VMReg()->next(2)  );
reg_def V3_K  ( SOC, SOC, Op_VecA, 3,  v3->as_VMReg()->next(3)  );

reg_def V4    ( SOC, SOC, Op_VecA, 4,  v4->as_VMReg()           );
reg_def V4_H  ( SOC, SOC, Op_VecA, 4,  v4->as_VMReg()->next()   );
reg_def V4_J  ( SOC, SOC, Op_VecA, 4,  v4->as_VMReg()->next(2)  );
reg_def V4_K  ( SOC, SOC, Op_VecA, 4,  v4->as_VMReg()->next(3)  );

reg_def V5    ( SOC, SOC, Op_VecA, 5,  v5->as_VMReg()          );
reg_def V5_H  ( SOC, SOC, Op_VecA, 5,  v5->as_VMReg()->next()   );
reg_def V5_J  ( SOC, SOC, Op_VecA, 5,  v5->as_VMReg()->next(2)  );
reg_def V5_K  ( SOC, SOC, Op_VecA, 5,  v5->as_VMReg()->next(3)  );

reg_def V6    ( SOC, SOC, Op_VecA, 6,  v6->as_VMReg()           );
reg_def V6_H  ( SOC, SOC, Op_VecA, 6,  v6->as_VMReg()->next()   );
reg_def V6_J  ( SOC, SOC, Op_VecA, 6,  v6->as_VMReg()->next(2)  );
reg_def V6_K  ( SOC, SOC, Op_VecA, 6,  v6->as_VMReg()->next(3)  );

reg_def V7    ( SOC, SOC, Op_VecA, 7,  v7->as_VMReg()          );
reg_def V7_H  ( SOC, SOC, Op_VecA, 7,  v7->as_VMReg()->next()   );
reg_def V7_J  ( SOC, SOC, Op_VecA, 7,  v7->as_VMReg()->next(2)  );
reg_def V7_K  ( SOC, SOC, Op_VecA, 7,  v7->as_VMReg()->next(3)  );

reg_def V8    ( SOC, SOC, Op_VecA, 8,  v8->as_VMReg()           );
reg_def V8_H  ( SOC, SOC, Op_VecA, 8,  v8->as_VMReg()->next()   );
reg_def V8_J  ( SOC, SOC, Op_VecA, 8,  v8->as_VMReg()->next(2)  );
reg_def V8_K  ( SOC, SOC, Op_VecA, 8,  v8->as_VMReg()->next(3)  );

reg_def V9    ( SOC, SOC, Op_VecA, 9,  v9->as_VMReg()           );
reg_def V9_H  ( SOC, SOC, Op_VecA, 9,  v9->as_VMReg()->next()   );
reg_def V9_J  ( SOC, SOC, Op_VecA, 9,  v9->as_VMReg()->next(2)  );
reg_def V9_K  ( SOC, SOC, Op_VecA, 9,  v9->as_VMReg()->next(3)  );

reg_def V10   ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()          );
reg_def V10_H ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next()  );
reg_def V10_J ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next(2) );
reg_def V10_K ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next(3) );

reg_def V11   ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()          );
reg_def V11_H ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next()  );
reg_def V11_J ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next(2) );
reg_def V11_K ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next(3) );

reg_def V12   ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()          );
reg_def V12_H ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next()  );
reg_def V12_J ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next(2) );
reg_def V12_K ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next(3) );

reg_def V13   ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()          );
reg_def V13_H ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next()  );
reg_def V13_J ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next(2) );
reg_def V13_K ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next(3) );

reg_def V14   ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()          );
reg_def V14_H ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next()  );
reg_def V14_J ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next(2) );
reg_def V14_K ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next(3) );

reg_def V15   ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()          );
reg_def V15_H ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next()  );
reg_def V15_J ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next(2) );
reg_def V15_K ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next(3) );

reg_def V16   ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()          );
reg_def V16_H ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next()  );
reg_def V16_J ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next(2) );
reg_def V16_K ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next(3) );

reg_def V17   ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()          );
reg_def V17_H ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next()  );
reg_def V17_J ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next(2) );
reg_def V17_K ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next(3) );

reg_def V18   ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()          );
reg_def V18_H ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next()  );
reg_def V18_J ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next(2) );
reg_def V18_K ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next(3) );

reg_def V19   ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()          );
reg_def V19_H ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next()  );
reg_def V19_J ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next(2) );
reg_def V19_K ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next(3) );

reg_def V20   ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()          );
reg_def V20_H ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next()  );
reg_def V20_J ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next(2) );
reg_def V20_K ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next(3) );

reg_def V21   ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()          );
reg_def V21_H ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next()  );
reg_def V21_J ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next(2) );
reg_def V21_K ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next(3) );

reg_def V22   ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()          );
reg_def V22_H ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next()  );
reg_def V22_J ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next(2) );
reg_def V22_K ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next(3) );

reg_def V23   ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()          );
reg_def V23_H ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next()  );
reg_def V23_J ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next(2) );
reg_def V23_K ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next(3) );

reg_def V24   ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()          );
reg_def V24_H ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next()  );
reg_def V24_J ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next(2) );
reg_def V24_K ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next(3) );

reg_def V25   ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()          );
reg_def V25_H ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next()  );
reg_def V25_J ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next(2) );
reg_def V25_K ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next(3) );

reg_def V26   ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()          );
reg_def V26_H ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next()  );
reg_def V26_J ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next(2) );
reg_def V26_K ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next(3) );

reg_def V27   ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()          );
reg_def V27_H ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next()  );
reg_def V27_J ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next(2) );
reg_def V27_K ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next(3) );

reg_def V28   ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()          );
reg_def V28_H ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next()  );
reg_def V28_J ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next(2) );
reg_def V28_K ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next(3) );

reg_def V29   ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()          );
reg_def V29_H ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next()  );
reg_def V29_J ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next(2) );
reg_def V29_K ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next(3) );

reg_def V30   ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()          );
reg_def V30_H ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next()  );
reg_def V30_J ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next(2) );
reg_def V30_K ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next(3) );

reg_def V31   ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()          );
reg_def V31_H ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next()  );
reg_def V31_J ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next(2) );
reg_def V31_K ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next(3) );

// ----------------------------
// Special Registers
// ----------------------------

// On riscv, the physical flag register is missing, so we use t1 instead,
// to bridge the RegFlag semantics in share/opto

reg_def RFLAGS   (SOC, SOC, Op_RegFlags, 6, x6->as_VMReg()        );

// Specify priority of register selection within phases of register
// allocation.  Highest priority is first.  A useful heuristic is to
// give registers a low priority when they are required by machine
// instructions, like EAX and EDX on I486, and choose no-save registers
// before save-on-call, & save-on-call before save-on-entry.  Registers
// which participate in fixed calling sequences should come last.
// Registers which are used as pairs must fall on an even boundary.

alloc_class chunk0(
    // volatiles
    R7,  R7_H,
    R28, R28_H,
    R29, R29_H,
    R30, R30_H,
    R31, R31_H,

    // arg registers
    R10, R10_H,
    R11, R11_H,
    R12, R12_H,
    R13, R13_H,
    R14, R14_H,
    R15, R15_H,
    R16, R16_H,
    R17, R17_H,

    // non-volatiles
    R9,  R9_H,
    R18, R18_H,
    R19, R19_H,
    R20, R20_H,
    R21, R21_H,
    R22, R22_H,
    R24, R24_H,
    R25, R25_H,
    R26, R26_H,

    // non-allocatable registers
    R23, R23_H, // java thread
    R27, R27_H, // heapbase
    R4,  R4_H,  // thread
    R8,  R8_H,  // fp
    R0,  R0_H,  // zero
    R1,  R1_H,  // ra
    R2,  R2_H,  // sp
    R3,  R3_H,  // gp
);

alloc_class chunk1(

    // no save
    F0,  F0_H,
    F1,  F1_H,
    F2,  F2_H,
    F3,  F3_H,
    F4,  F4_H,
    F5,  F5_H,
    F6,  F6_H,
    F7,  F7_H,
    F28, F28_H,
    F29, F29_H,
    F30, F30_H,
    F31, F31_H,

    // arg registers
    F10, F10_H,
    F11, F11_H,
    F12, F12_H,
    F13, F13_H,
    F14, F14_H,
    F15, F15_H,
    F16, F16_H,
    F17, F17_H,

    // non-volatiles
    F8,  F8_H,
    F9,  F9_H,
    F18, F18_H,
    F19, F19_H,
    F20, F20_H,
    F21, F21_H,
    F22, F22_H,
    F23, F23_H,
    F24, F24_H,
    F25, F25_H,
    F26, F26_H,
    F27, F27_H,
);

alloc_class chunk2(
    V0, V0_H, V0_J, V0_K,
    V1, V1_H, V1_J, V1_K,
    V2, V2_H, V2_J, V2_K,
    V3, V3_H, V3_J, V3_K,
    V4, V4_H, V4_J, V4_K,
    V5, V5_H, V5_J, V5_K,
    V6, V6_H, V6_J, V6_K,
    V7, V7_H, V7_J, V7_K,
    V8, V8_H, V8_J, V8_K,
    V9, V9_H, V9_J, V9_K,
    V10, V10_H, V10_J, V10_K,
    V11, V11_H, V11_J, V11_K,
    V12, V12_H, V12_J, V12_K,
    V13, V13_H, V13_J, V13_K,
    V14, V14_H, V14_J, V14_K,
    V15, V15_H, V15_J, V15_K,
    V16, V16_H, V16_J, V16_K,
    V17, V17_H, V17_J, V17_K,
    V18, V18_H, V18_J, V18_K,
    V19, V19_H, V19_J, V19_K,
    V20, V20_H, V20_J, V20_K,
    V21, V21_H, V21_J, V21_K,
    V22, V22_H, V22_J, V22_K,
    V23, V23_H, V23_J, V23_K,
    V24, V24_H, V24_J, V24_K,
    V25, V25_H, V25_J, V25_K,
    V26, V26_H, V26_J, V26_K,
    V27, V27_H, V27_J, V27_K,
    V28, V28_H, V28_J, V28_K,
    V29, V29_H, V29_J, V29_K,
    V30, V30_H, V30_J, V30_K,
    V31, V31_H, V31_J, V31_K,
);

alloc_class chunk3(RFLAGS);

//----------Architecture Description Register Classes--------------------------
// Several register classes are automatically defined based upon information in
// this architecture description.
// 1) reg_class inline_cache_reg           ( /* as def'd in frame section */ )
// 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ )
//

// Class for all 32 bit general purpose registers
reg_class all_reg32(
    R0,
    R1,
    R2,
    R3,
    R4,
    R7,
    R8,
    R9,
    R10,
    R11,
    R12,
    R13,
    R14,
    R15,
    R16,
    R17,
    R18,
    R19,
    R20,
    R21,
    R22,
    R23,
    R24,
    R25,
    R26,
    R27,
    R28,
    R29,
    R30,
    R31
);

// Class for any 32 bit integer registers (excluding zr)
reg_class any_reg32 %{
  return _ANY_REG32_mask;
%}

// Singleton class for R10 int register
reg_class int_r10_reg(R10);

// Singleton class for R12 int register
reg_class int_r12_reg(R12);

// Singleton class for R13 int register
reg_class int_r13_reg(R13);

// Singleton class for R14 int register
reg_class int_r14_reg(R14);

// Class for all long integer registers
reg_class all_reg(
    R0,  R0_H,
    R1,  R1_H,
    R2,  R2_H,
    R3,  R3_H,
    R4,  R4_H,
    R7,  R7_H,
    R8,  R8_H,
    R9,  R9_H,
    R10, R10_H,
    R11, R11_H,
    R12, R12_H,
    R13, R13_H,
    R14, R14_H,
    R15, R15_H,
    R16, R16_H,
    R17, R17_H,
    R18, R18_H,
    R19, R19_H,
    R20, R20_H,
    R21, R21_H,
    R22, R22_H,
    R23, R23_H,
    R24, R24_H,
    R25, R25_H,
    R26, R26_H,
    R27, R27_H,
    R28, R28_H,
    R29, R29_H,
    R30, R30_H,
    R31, R31_H
);

// Class for all long integer registers (excluding zr)
reg_class any_reg %{
  return _ANY_REG_mask;
%}

// Class for non-allocatable 32 bit registers
reg_class non_allocatable_reg32(
    R0,                       // zr
    R1,                       // ra
    R2,                       // sp
    R3,                       // gp
    R4,                       // tp
    R23                       // java thread
);

// Class for non-allocatable 64 bit registers
reg_class non_allocatable_reg(
    R0,  R0_H,                // zr
    R1,  R1_H,                // ra
    R2,  R2_H,                // sp
    R3,  R3_H,                // gp
    R4,  R4_H,                // tp
    R23, R23_H                // java thread
);

reg_class no_special_reg32 %{
  return _NO_SPECIAL_REG32_mask;
%}

reg_class no_special_reg %{
  return _NO_SPECIAL_REG_mask;
%}

reg_class ptr_reg %{
  return _PTR_REG_mask;
%}

reg_class no_special_ptr_reg %{
  return _NO_SPECIAL_PTR_REG_mask;
%}

// Class for 64 bit register r10
reg_class r10_reg(
    R10, R10_H
);

// Class for 64 bit register r11
reg_class r11_reg(
    R11, R11_H
);

// Class for 64 bit register r12
reg_class r12_reg(
    R12, R12_H
);

// Class for 64 bit register r13
reg_class r13_reg(
    R13, R13_H
);

// Class for 64 bit register r14
reg_class r14_reg(
    R14, R14_H
);

// Class for 64 bit register r15
reg_class r15_reg(
    R15, R15_H
);

// Class for 64 bit register r16
reg_class r16_reg(
    R16, R16_H
);

// Class for method register
reg_class method_reg(
    R31, R31_H
);

// Class for heapbase register
reg_class heapbase_reg(
    R27, R27_H
);

// Class for java thread register
reg_class java_thread_reg(
    R23, R23_H
);

reg_class r28_reg(
    R28, R28_H
);

reg_class r29_reg(
    R29, R29_H
);

reg_class r30_reg(
    R30, R30_H
);

reg_class r31_reg(
    R31, R31_H
);

// Class for zero registesr
reg_class zr_reg(
    R0, R0_H
);

// Class for thread register
reg_class thread_reg(
    R4, R4_H
);

// Class for frame pointer register
reg_class fp_reg(
    R8, R8_H
);

// Class for link register
reg_class ra_reg(
    R1, R1_H
);

// Class for long sp register
reg_class sp_reg(
    R2, R2_H
);

// Class for all float registers
reg_class float_reg(
    F0,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,
    F25,
    F26,
    F27,
    F28,
    F29,
    F30,
    F31
);

// Double precision float registers have virtual `high halves' that
// are needed by the allocator.
// Class for all double registers
reg_class double_reg(
    F0,  F0_H,
    F1,  F1_H,
    F2,  F2_H,
    F3,  F3_H,
    F4,  F4_H,
    F5,  F5_H,
    F6,  F6_H,
    F7,  F7_H,
    F8,  F8_H,
    F9,  F9_H,
    F10, F10_H,
    F11, F11_H,
    F12, F12_H,
    F13, F13_H,
    F14, F14_H,
    F15, F15_H,
    F16, F16_H,
    F17, F17_H,
    F18, F18_H,
    F19, F19_H,
    F20, F20_H,
    F21, F21_H,
    F22, F22_H,
    F23, F23_H,
    F24, F24_H,
    F25, F25_H,
    F26, F26_H,
    F27, F27_H,
    F28, F28_H,
    F29, F29_H,
    F30, F30_H,
    F31, F31_H
);

// Class for all RVV vector registers
reg_class vectora_reg(
    V1, V1_H, V1_J, V1_K,
    V2, V2_H, V2_J, V2_K,
    V3, V3_H, V3_J, V3_K,
    V4, V4_H, V4_J, V4_K,
    V5, V5_H, V5_J, V5_K,
    V6, V6_H, V6_J, V6_K,
    V7, V7_H, V7_J, V7_K,
    V8, V8_H, V8_J, V8_K,
    V9, V9_H, V9_J, V9_K,
    V10, V10_H, V10_J, V10_K,
    V11, V11_H, V11_J, V11_K,
    V12, V12_H, V12_J, V12_K,
    V13, V13_H, V13_J, V13_K,
    V14, V14_H, V14_J, V14_K,
    V15, V15_H, V15_J, V15_K,
    V16, V16_H, V16_J, V16_K,
    V17, V17_H, V17_J, V17_K,
    V18, V18_H, V18_J, V18_K,
    V19, V19_H, V19_J, V19_K,
    V20, V20_H, V20_J, V20_K,
    V21, V21_H, V21_J, V21_K,
    V22, V22_H, V22_J, V22_K,
    V23, V23_H, V23_J, V23_K,
    V24, V24_H, V24_J, V24_K,
    V25, V25_H, V25_J, V25_K,
    V26, V26_H, V26_J, V26_K,
    V27, V27_H, V27_J, V27_K,
    V28, V28_H, V28_J, V28_K,
    V29, V29_H, V29_J, V29_K,
    V30, V30_H, V30_J, V30_K,
    V31, V31_H, V31_J, V31_K
);

// Class for 64 bit register f0
reg_class f0_reg(
    F0, F0_H
);

// Class for 64 bit register f1
reg_class f1_reg(
    F1, F1_H
);

// Class for 64 bit register f2
reg_class f2_reg(
    F2, F2_H
);

// Class for 64 bit register f3
reg_class f3_reg(
    F3, F3_H
);

// class for vector register v1
reg_class v1_reg(
    V1, V1_H, V1_J, V1_K
);

// class for vector register v2
reg_class v2_reg(
    V2, V2_H, V2_J, V2_K
);

// class for vector register v3
reg_class v3_reg(
    V3, V3_H, V3_J, V3_K
);

// class for vector register v4
reg_class v4_reg(
    V4, V4_H, V4_J, V4_K
);

// class for vector register v5
reg_class v5_reg(
    V5, V5_H, V5_J, V5_K
);

// class for condition codes
reg_class reg_flags(RFLAGS);
%}

//----------DEFINITION BLOCK---------------------------------------------------
// Define name --> value mappings to inform the ADLC of an integer valued name
// Current support includes integer values in the range [0, 0x7FFFFFFF]
// Format:
//        int_def  <name>         ( <int_value>, <expression>);
// Generated Code in ad_<arch>.hpp
//        #define  <name>   (<expression>)
//        // value == <int_value>
// Generated code in ad_<arch>.cpp adlc_verification()
//        assert( <name> == <int_value>, "Expect (<expression>) to equal <int_value>");
//

// we follow the ppc-aix port in using a simple cost model which ranks
// register operations as cheap, memory ops as more expensive and
// branches as most expensive. the first two have a low as well as a
// normal cost. huge cost appears to be a way of saying don't do
// something

definitions %{
  // The default cost (of a register move instruction).
  int_def DEFAULT_COST         (  100,               100);
  int_def ALU_COST             (  100,  1 * DEFAULT_COST);          // unknown, const, arith, shift, slt,
                                                                    // multi, auipc, nop, logical, move
  int_def LOAD_COST            (  300,  3 * DEFAULT_COST);          // load, fpload
  int_def STORE_COST           (  100,  1 * DEFAULT_COST);          // store, fpstore
  int_def XFER_COST            (  300,  3 * DEFAULT_COST);          // mfc, mtc, fcvt, fmove, fcmp
  int_def BRANCH_COST          (  100,  1 * DEFAULT_COST);          // branch, jmp, call
  int_def IMUL_COST            ( 1000, 10 * DEFAULT_COST);          // imul
  int_def IDIVSI_COST          ( 3400, 34 * DEFAULT_COST);          // idivdi
  int_def IDIVDI_COST          ( 6600, 66 * DEFAULT_COST);          // idivsi
  int_def FMUL_SINGLE_COST     (  500,  5 * DEFAULT_COST);          // fadd, fmul, fmadd
  int_def FMUL_DOUBLE_COST     (  700,  7 * DEFAULT_COST);          // fadd, fmul, fmadd
  int_def FDIV_COST            ( 2000, 20 * DEFAULT_COST);          // fdiv
  int_def FSQRT_COST           ( 2500, 25 * DEFAULT_COST);          // fsqrt
  int_def VOLATILE_REF_COST    ( 1000, 10 * DEFAULT_COST);
%}



//----------SOURCE BLOCK-------------------------------------------------------
// This is a block of C++ code which provides values, functions, and
// definitions necessary in the rest of the architecture description

source_hpp %{

#include "asm/macroAssembler.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/cardTable.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "opto/addnode.hpp"
#include "opto/convertnode.hpp"
#include "runtime/objectMonitor.hpp"

extern RegMask _ANY_REG32_mask;
extern RegMask _ANY_REG_mask;
extern RegMask _PTR_REG_mask;
extern RegMask _NO_SPECIAL_REG32_mask;
extern RegMask _NO_SPECIAL_REG_mask;
extern RegMask _NO_SPECIAL_PTR_REG_mask;

class CallStubImpl {

  //--------------------------------------------------------------
  //---<  Used for optimization in Compile::shorten_branches  >---
  //--------------------------------------------------------------

 public:
  // Size of call trampoline stub.
  static uint size_call_trampoline() {
    return 0; // no call trampolines on this platform
  }

  // number of relocations needed by a call trampoline stub
  static uint reloc_call_trampoline() {
    return 0; // no call trampolines on this platform
  }
};

class HandlerImpl {

 public:

  static int emit_exception_handler(CodeBuffer &cbuf);
  static int emit_deopt_handler(CodeBuffer& cbuf);

  static uint size_exception_handler() {
    return MacroAssembler::far_branch_size();
  }

  static uint size_deopt_handler() {
    // count auipc + far branch
    return NativeInstruction::instruction_size + MacroAssembler::far_branch_size();
  }
};

class Node::PD {
public:
  enum NodeFlags {
    _last_flag = Node::_last_flag
  };
};

bool is_CAS(int opcode, bool maybe_volatile);

// predicate controlling translation of CompareAndSwapX
bool needs_acquiring_load_reserved(const Node *load);

// predicate controlling addressing modes
bool size_fits_all_mem_uses(AddPNode* addp, int shift);
%}

source %{

// Derived RegMask with conditionally allocatable registers

RegMask _ANY_REG32_mask;
RegMask _ANY_REG_mask;
RegMask _PTR_REG_mask;
RegMask _NO_SPECIAL_REG32_mask;
RegMask _NO_SPECIAL_REG_mask;
RegMask _NO_SPECIAL_PTR_REG_mask;

void reg_mask_init() {

  _ANY_REG32_mask = _ALL_REG32_mask;
  _ANY_REG32_mask.Remove(OptoReg::as_OptoReg(x0->as_VMReg()));

  _ANY_REG_mask = _ALL_REG_mask;
  _ANY_REG_mask.SUBTRACT(_ZR_REG_mask);

  _PTR_REG_mask = _ALL_REG_mask;
  _PTR_REG_mask.SUBTRACT(_ZR_REG_mask);

  _NO_SPECIAL_REG32_mask = _ALL_REG32_mask;
  _NO_SPECIAL_REG32_mask.SUBTRACT(_NON_ALLOCATABLE_REG32_mask);

  _NO_SPECIAL_REG_mask = _ALL_REG_mask;
  _NO_SPECIAL_REG_mask.SUBTRACT(_NON_ALLOCATABLE_REG_mask);

  _NO_SPECIAL_PTR_REG_mask = _ALL_REG_mask;
  _NO_SPECIAL_PTR_REG_mask.SUBTRACT(_NON_ALLOCATABLE_REG_mask);

  // x27 is not allocatable when compressed oops is on
  if (UseCompressedOops) {
    _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg()));
    _NO_SPECIAL_REG_mask.SUBTRACT(_HEAPBASE_REG_mask);
    _NO_SPECIAL_PTR_REG_mask.SUBTRACT(_HEAPBASE_REG_mask);
  }

  // x8 is not allocatable when PreserveFramePointer is on
  if (PreserveFramePointer) {
    _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg()));
    _NO_SPECIAL_REG_mask.SUBTRACT(_FP_REG_mask);
    _NO_SPECIAL_PTR_REG_mask.SUBTRACT(_FP_REG_mask);
  }
}

void PhaseOutput::pd_perform_mach_node_analysis() {
}

int MachNode::pd_alignment_required() const {
  return 1;
}

int MachNode::compute_padding(int current_offset) const {
  return 0;
}

// is_CAS(int opcode, bool maybe_volatile)
//
// return true if opcode is one of the possible CompareAndSwapX
// values otherwise false.
bool is_CAS(int opcode, bool maybe_volatile)
{
  switch (opcode) {
    // We handle these
    case Op_CompareAndSwapI:
    case Op_CompareAndSwapL:
    case Op_CompareAndSwapP:
    case Op_CompareAndSwapN:
    case Op_ShenandoahCompareAndSwapP:
    case Op_ShenandoahCompareAndSwapN:
    case Op_CompareAndSwapB:
    case Op_CompareAndSwapS:
    case Op_GetAndSetI:
    case Op_GetAndSetL:
    case Op_GetAndSetP:
    case Op_GetAndSetN:
    case Op_GetAndAddI:
    case Op_GetAndAddL:
      return true;
    case Op_CompareAndExchangeI:
    case Op_CompareAndExchangeN:
    case Op_CompareAndExchangeB:
    case Op_CompareAndExchangeS:
    case Op_CompareAndExchangeL:
    case Op_CompareAndExchangeP:
    case Op_WeakCompareAndSwapB:
    case Op_WeakCompareAndSwapS:
    case Op_WeakCompareAndSwapI:
    case Op_WeakCompareAndSwapL:
    case Op_WeakCompareAndSwapP:
    case Op_WeakCompareAndSwapN:
    case Op_ShenandoahWeakCompareAndSwapP:
    case Op_ShenandoahWeakCompareAndSwapN:
    case Op_ShenandoahCompareAndExchangeP:
    case Op_ShenandoahCompareAndExchangeN:
      return maybe_volatile;
    default:
      return false;
  }
}

// predicate controlling translation of CAS
//
// returns true if CAS needs to use an acquiring load otherwise false
bool needs_acquiring_load_reserved(const Node *n)
{
  assert(n != NULL && is_CAS(n->Opcode(), true), "expecting a compare and swap");

  LoadStoreNode* ldst = n->as_LoadStore();
  if (n != NULL && is_CAS(n->Opcode(), false)) {
    assert(ldst != NULL && ldst->trailing_membar() != NULL, "expected trailing membar");
  } else {
    return ldst != NULL && ldst->trailing_membar() != NULL;
  }
  // so we can just return true here
  return true;
}
#define __ _masm.

// advance declarations for helper functions to convert register
// indices to register objects

// the ad file has to provide implementations of certain methods
// expected by the generic code
//
// REQUIRED FUNCTIONALITY

//=============================================================================

// !!!!! Special hack to get all types of calls to specify the byte offset
//       from the start of the call to the point where the return address
//       will point.

int MachCallStaticJavaNode::ret_addr_offset()
{
  // jal
  return 1 * NativeInstruction::instruction_size;
}

int MachCallDynamicJavaNode::ret_addr_offset()
{
  return 7 * NativeInstruction::instruction_size; // movptr, jal
}

int MachCallRuntimeNode::ret_addr_offset() {
  // for generated stubs the call will be
  //   jal(addr)
  // or with far branches
  //   jal(trampoline_stub)
  // for real runtime callouts it will be 11 instructions
  // see riscv_enc_java_to_runtime
  //   la(t1, retaddr)                ->  auipc + addi
  //   la(t0, RuntimeAddress(addr))   ->  lui + addi + slli + addi + slli + addi
  //   addi(sp, sp, -2 * wordSize)    ->  addi
  //   sd(t1, Address(sp, wordSize))  ->  sd
  //   jalr(t0)                       ->  jalr
  CodeBlob *cb = CodeCache::find_blob(_entry_point);
  if (cb != NULL) {
    return 1 * NativeInstruction::instruction_size;
  } else {
    return 11 * NativeInstruction::instruction_size;
  }
}

//
// Compute padding required for nodes which need alignment
//

// With RVC a call instruction may get 2-byte aligned.
// The address of the call instruction needs to be 4-byte aligned to
// ensure that it does not span a cache line so that it can be patched.
int CallStaticJavaDirectNode::compute_padding(int current_offset) const
{
  // to make sure the address of jal 4-byte aligned.
  return align_up(current_offset, alignment_required()) - current_offset;
}

// With RVC a call instruction may get 2-byte aligned.
// The address of the call instruction needs to be 4-byte aligned to
// ensure that it does not span a cache line so that it can be patched.
int CallDynamicJavaDirectNode::compute_padding(int current_offset) const
{
  // skip the movptr in MacroAssembler::ic_call():
  // lui + addi + slli + addi + slli + addi
  // Though movptr() has already 4-byte aligned with or without RVC,
  // We need to prevent from further changes by explicitly calculating the size.
  const int movptr_size = 6 * NativeInstruction::instruction_size;
  current_offset += movptr_size;
  // to make sure the address of jal 4-byte aligned.
  return align_up(current_offset, alignment_required()) - current_offset;
}

//=============================================================================

#ifndef PRODUCT
void MachBreakpointNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
  assert_cond(st != NULL);
  st->print("BREAKPOINT");
}
#endif

void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
  C2_MacroAssembler _masm(&cbuf);
  __ ebreak();
}

uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const {
  return MachNode::size(ra_);
}

//=============================================================================

#ifndef PRODUCT
  void MachNopNode::format(PhaseRegAlloc*, outputStream* st) const {
    st->print("nop \t# %d bytes pad for loops and calls", _count);
  }
#endif

  void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc*) const {
    C2_MacroAssembler _masm(&cbuf);
    Assembler::CompressibleRegion cr(&_masm); // nops shall be 2-byte under RVC for alignment purposes.
    for (int i = 0; i < _count; i++) {
      __ nop();
    }
  }

  uint MachNopNode::size(PhaseRegAlloc*) const {
    return _count * (UseRVC ? NativeInstruction::compressed_instruction_size : NativeInstruction::instruction_size);
  }

//=============================================================================
const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty;

int ConstantTable::calculate_table_base_offset() const {
  return 0;  // absolute addressing, no offset
}

bool MachConstantBaseNode::requires_postalloc_expand() const { return false; }
void MachConstantBaseNode::postalloc_expand(GrowableArray <Node *> *nodes, PhaseRegAlloc *ra_) {
  ShouldNotReachHere();
}

void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
  // Empty encoding
}

uint MachConstantBaseNode::size(PhaseRegAlloc* ra_) const {
  return 0;
}

#ifndef PRODUCT
void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const {
  assert_cond(st != NULL);
  st->print("-- \t// MachConstantBaseNode (empty encoding)");
}
#endif

#ifndef PRODUCT
void MachPrologNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
  assert_cond(st != NULL && ra_ != NULL);
  Compile* C = ra_->C;

  int framesize = C->output()->frame_slots() << LogBytesPerInt;

  if (C->output()->need_stack_bang(framesize)) {
    st->print("# stack bang size=%d\n\t", framesize);
  }

  st->print("sd  fp, [sp, #%d]\n\t", - 2 * wordSize);
  st->print("sd  ra, [sp, #%d]\n\t", - wordSize);
  if (PreserveFramePointer) { st->print("sub  fp, sp, #%d\n\t", 2 * wordSize); }
  st->print("sub sp, sp, #%d\n\t", framesize);

  if (C->stub_function() == NULL && BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) {
    st->print("ld  t0, [guard]\n\t");
    st->print("membar LoadLoad\n\t");
    st->print("ld  t1, [xthread, #thread_disarmed_offset]\n\t");
    st->print("beq t0, t1, skip\n\t");
    st->print("jalr #nmethod_entry_barrier_stub\n\t");
    st->print("j skip\n\t");
    st->print("guard: int\n\t");
    st->print("skip:\n\t");
  }
}
#endif

void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
  assert_cond(ra_ != NULL);
  Compile* C = ra_->C;
  C2_MacroAssembler _masm(&cbuf);

  // n.b. frame size includes space for return pc and fp
  const int framesize = C->output()->frame_size_in_bytes();

  // insert a nop at the start of the prolog so we can patch in a
  // branch if we need to invalidate the method later
  {
    Assembler::IncompressibleRegion ir(&_masm);  // keep the nop as 4 bytes for patching.
    MacroAssembler::assert_alignment(__ pc());
    __ nop();  // 4 bytes
  }

  assert_cond(C != NULL);

  if (C->clinit_barrier_on_entry()) {
    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");

    Label L_skip_barrier;

    __ mov_metadata(t1, C->method()->holder()->constant_encoding());
    __ clinit_barrier(t1, t0, &L_skip_barrier);
    __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
    __ bind(L_skip_barrier);
  }

  int bangsize = C->output()->bang_size_in_bytes();
  if (C->output()->need_stack_bang(bangsize)) {
    __ generate_stack_overflow_check(bangsize);
  }

  __ build_frame(framesize);

  if (C->stub_function() == NULL) {
    BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
    if (BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) {
      // Dummy labels for just measuring the code size
      Label dummy_slow_path;
      Label dummy_continuation;
      Label dummy_guard;
      Label* slow_path = &dummy_slow_path;
      Label* continuation = &dummy_continuation;
      Label* guard = &dummy_guard;
      if (!Compile::current()->output()->in_scratch_emit_size()) {
        // Use real labels from actual stub when not emitting code for purpose of measuring its size
        C2EntryBarrierStub* stub = Compile::current()->output()->entry_barrier_table()->add_entry_barrier();
        slow_path = &stub->slow_path();
        continuation = &stub->continuation();
        guard = &stub->guard();
      }
      // In the C2 code, we move the non-hot part of nmethod entry barriers out-of-line to a stub.
      bs->nmethod_entry_barrier(&_masm, slow_path, continuation, guard);
    }
  }

  if (VerifyStackAtCalls) {
    Unimplemented();
  }

  C->output()->set_frame_complete(cbuf.insts_size());

  if (C->has_mach_constant_base_node()) {
    // NOTE: We set the table base offset here because users might be
    // emitted before MachConstantBaseNode.
    ConstantTable& constant_table = C->output()->constant_table();
    constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
  }
}

uint MachPrologNode::size(PhaseRegAlloc* ra_) const
{
  assert_cond(ra_ != NULL);
  return MachNode::size(ra_); // too many variables; just compute it
                              // the hard way
}

int MachPrologNode::reloc() const
{
  return 0;
}

//=============================================================================

#ifndef PRODUCT
void MachEpilogNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
  assert_cond(st != NULL && ra_ != NULL);
  Compile* C = ra_->C;
  assert_cond(C != NULL);
  int framesize = C->output()->frame_size_in_bytes();

  st->print("# pop frame %d\n\t", framesize);

  if (framesize == 0) {
    st->print("ld  ra, [sp,#%d]\n\t", (2 * wordSize));
    st->print("ld  fp, [sp,#%d]\n\t", (3 * wordSize));
    st->print("add sp, sp, #%d\n\t", (2 * wordSize));
  } else {
    st->print("add  sp, sp, #%d\n\t", framesize);
    st->print("ld  ra, [sp,#%d]\n\t", - 2 * wordSize);
    st->print("ld  fp, [sp,#%d]\n\t", - wordSize);
  }

  if (do_polling() && C->is_method_compilation()) {
    st->print("# test polling word\n\t");
    st->print("ld t0, [xthread,#%d]\n\t", in_bytes(JavaThread::polling_word_offset()));
    st->print("bgtu sp, t0, #slow_path");
  }
}
#endif

void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
  assert_cond(ra_ != NULL);
  Compile* C = ra_->C;
  C2_MacroAssembler _masm(&cbuf);
  assert_cond(C != NULL);
  int framesize = C->output()->frame_size_in_bytes();

  __ remove_frame(framesize);

  if (StackReservedPages > 0 && C->has_reserved_stack_access()) {
    __ reserved_stack_check();
  }

  if (do_polling() && C->is_method_compilation()) {
    Label dummy_label;
    Label* code_stub = &dummy_label;
    if (!C->output()->in_scratch_emit_size()) {
      code_stub = &C->output()->safepoint_poll_table()->add_safepoint(__ offset());
    }
    __ relocate(relocInfo::poll_return_type);
    __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */);
  }
}

uint MachEpilogNode::size(PhaseRegAlloc *ra_) const {
  assert_cond(ra_ != NULL);
  // Variable size. Determine dynamically.
  return MachNode::size(ra_);
}

int MachEpilogNode::reloc() const {
  // Return number of relocatable values contained in this instruction.
  return 1; // 1 for polling page.
}
const Pipeline * MachEpilogNode::pipeline() const {
  return MachNode::pipeline_class();
}

//=============================================================================

// Figure out which register class each belongs in: rc_int, rc_float or
// rc_stack.
enum RC { rc_bad, rc_int, rc_float, rc_vector, rc_stack };

static enum RC rc_class(OptoReg::Name reg) {

  if (reg == OptoReg::Bad) {
    return rc_bad;
  }

  // we have 30 int registers * 2 halves
  // (t0 and t1 are omitted)
  int slots_of_int_registers = Register::max_slots_per_register * (Register::number_of_registers - 2);
  if (reg < slots_of_int_registers) {
    return rc_int;
  }

  // we have 32 float register * 2 halves
  int slots_of_float_registers = FloatRegister::max_slots_per_register * FloatRegister::number_of_registers;
  if (reg < slots_of_int_registers + slots_of_float_registers) {
    return rc_float;
  }

  // we have 32 vector register * 4 halves
  int slots_of_vector_registers = VectorRegister::max_slots_per_register * VectorRegister::number_of_registers;
  if (reg < slots_of_int_registers + slots_of_float_registers + slots_of_vector_registers) {
    return rc_vector;
  }

  // Between vector regs & stack is the flags regs.
  assert(OptoReg::is_stack(reg), "blow up if spilling flags");

  return rc_stack;
}

uint MachSpillCopyNode::implementation(CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream *st) const {
  assert_cond(ra_ != NULL);
  Compile* C = ra_->C;

  // Get registers to move.
  OptoReg::Name src_hi = ra_->get_reg_second(in(1));
  OptoReg::Name src_lo = ra_->get_reg_first(in(1));
  OptoReg::Name dst_hi = ra_->get_reg_second(this);
  OptoReg::Name dst_lo = ra_->get_reg_first(this);

  enum RC src_hi_rc = rc_class(src_hi);
  enum RC src_lo_rc = rc_class(src_lo);
  enum RC dst_hi_rc = rc_class(dst_hi);
  enum RC dst_lo_rc = rc_class(dst_lo);

  assert(src_lo != OptoReg::Bad && dst_lo != OptoReg::Bad, "must move at least 1 register");

  if (src_hi != OptoReg::Bad) {
    assert((src_lo & 1) == 0 && src_lo + 1 == src_hi &&
           (dst_lo & 1) == 0 && dst_lo + 1 == dst_hi,
           "expected aligned-adjacent pairs");
  }

  if (src_lo == dst_lo && src_hi == dst_hi) {
    return 0;            // Self copy, no move.
  }

  bool is64 = (src_lo & 1) == 0 && src_lo + 1 == src_hi &&
              (dst_lo & 1) == 0 && dst_lo + 1 == dst_hi;
  int src_offset = ra_->reg2offset(src_lo);
  int dst_offset = ra_->reg2offset(dst_lo);

  if (bottom_type()->isa_vect() != NULL) {
    uint ireg = ideal_reg();
    if (ireg == Op_VecA && cbuf) {
      C2_MacroAssembler _masm(cbuf);
      int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
      if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) {
        // stack to stack
        __ spill_copy_vector_stack_to_stack(src_offset, dst_offset,
                                            vector_reg_size_in_bytes);
      } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_stack) {
        // vpr to stack
        __ spill(as_VectorRegister(Matcher::_regEncode[src_lo]), ra_->reg2offset(dst_lo));
      } else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vector) {
        // stack to vpr
        __ unspill(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo));
      } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) {
        // vpr to vpr
        __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo]));
      } else {
        ShouldNotReachHere();
      }
    }
  } else if (cbuf != NULL) {
    C2_MacroAssembler _masm(cbuf);
    switch (src_lo_rc) {
      case rc_int:
        if (dst_lo_rc == rc_int) {  // gpr --> gpr copy
          if (!is64 && this->ideal_reg() != Op_RegI) { // zero extended for narrow oop or klass
            __ zero_extend(as_Register(Matcher::_regEncode[dst_lo]), as_Register(Matcher::_regEncode[src_lo]), 32);
          } else {
            __ mv(as_Register(Matcher::_regEncode[dst_lo]), as_Register(Matcher::_regEncode[src_lo]));
          }
        } else if (dst_lo_rc == rc_float) { // gpr --> fpr copy
          if (is64) {
            __ fmv_d_x(as_FloatRegister(Matcher::_regEncode[dst_lo]),
                       as_Register(Matcher::_regEncode[src_lo]));
          } else {
            __ fmv_w_x(as_FloatRegister(Matcher::_regEncode[dst_lo]),
                       as_Register(Matcher::_regEncode[src_lo]));
          }
        } else {                    // gpr --> stack spill
          assert(dst_lo_rc == rc_stack, "spill to bad register class");
          __ spill(as_Register(Matcher::_regEncode[src_lo]), is64, dst_offset);
        }
        break;
      case rc_float:
        if (dst_lo_rc == rc_int) {  // fpr --> gpr copy
          if (is64) {
            __ fmv_x_d(as_Register(Matcher::_regEncode[dst_lo]),
                       as_FloatRegister(Matcher::_regEncode[src_lo]));
          } else {
            __ fmv_x_w(as_Register(Matcher::_regEncode[dst_lo]),
                       as_FloatRegister(Matcher::_regEncode[src_lo]));
          }
        } else if (dst_lo_rc == rc_float) { // fpr --> fpr copy
          if (is64) {
            __ fmv_d(as_FloatRegister(Matcher::_regEncode[dst_lo]),
                     as_FloatRegister(Matcher::_regEncode[src_lo]));
          } else {
            __ fmv_s(as_FloatRegister(Matcher::_regEncode[dst_lo]),
                     as_FloatRegister(Matcher::_regEncode[src_lo]));
          }
        } else {                    // fpr --> stack spill
          assert(dst_lo_rc == rc_stack, "spill to bad register class");
          __ spill(as_FloatRegister(Matcher::_regEncode[src_lo]),
                   is64, dst_offset);
        }
        break;
      case rc_stack:
        if (dst_lo_rc == rc_int) {  // stack --> gpr load
          if (this->ideal_reg() == Op_RegI) {
            __ unspill(as_Register(Matcher::_regEncode[dst_lo]), is64, src_offset);
          } else { // // zero extended for narrow oop or klass
            __ unspillu(as_Register(Matcher::_regEncode[dst_lo]), is64, src_offset);
          }
        } else if (dst_lo_rc == rc_float) { // stack --> fpr load
          __ unspill(as_FloatRegister(Matcher::_regEncode[dst_lo]),
                     is64, src_offset);
        } else {                    // stack --> stack copy
          assert(dst_lo_rc == rc_stack, "spill to bad register class");
          if (this->ideal_reg() == Op_RegI) {
            __ unspill(t0, is64, src_offset);
          } else { // zero extended for narrow oop or klass
            __ unspillu(t0, is64, src_offset);
          }
          __ spill(t0, is64, dst_offset);
        }
        break;
      default:
        ShouldNotReachHere();
    }
  }

  if (st != NULL) {
    st->print("spill ");
    if (src_lo_rc == rc_stack) {
      st->print("[sp, #%d] -> ", src_offset);
    } else {
      st->print("%s -> ", Matcher::regName[src_lo]);
    }
    if (dst_lo_rc == rc_stack) {
      st->print("[sp, #%d]", dst_offset);
    } else {
      st->print("%s", Matcher::regName[dst_lo]);
    }
    if (bottom_type()->isa_vect() != NULL) {
      int vsize = 0;
      if (ideal_reg() == Op_VecA) {
        vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8;
      } else {
        ShouldNotReachHere();
      }
      st->print("\t# vector spill size = %d", vsize);
    } else {
      st->print("\t# spill size = %d", is64 ? 64 : 32);
    }
  }

  return 0;
}

#ifndef PRODUCT
void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
  if (ra_ == NULL) {
    st->print("N%d = SpillCopy(N%d)", _idx, in(1)->_idx);
  } else {
    implementation(NULL, ra_, false, st);
  }
}
#endif

void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
  implementation(&cbuf, ra_, false, NULL);
}

uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const {
  return MachNode::size(ra_);
}

//=============================================================================

#ifndef PRODUCT
void BoxLockNode::format(PhaseRegAlloc *ra_, outputStream *st) const {
  assert_cond(ra_ != NULL && st != NULL);
  int offset = ra_->reg2offset(in_RegMask(0).find_first_elem());
  int reg = ra_->get_reg_first(this);
  st->print("add %s, sp, #%d\t# box lock",
            Matcher::regName[reg], offset);
}
#endif

void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
  C2_MacroAssembler _masm(&cbuf);
  Assembler::IncompressibleRegion ir(&_masm);  // Fixed length: see BoxLockNode::size()

  assert_cond(ra_ != NULL);
  int offset = ra_->reg2offset(in_RegMask(0).find_first_elem());
  int reg    = ra_->get_encode(this);

  if (is_imm_in_range(offset, 12, 0)) {
    __ addi(as_Register(reg), sp, offset);
  } else if (is_imm_in_range(offset, 32, 0)) {
    __ li32(t0, offset);
    __ add(as_Register(reg), sp, t0);
  } else {
    ShouldNotReachHere();
  }
}

uint BoxLockNode::size(PhaseRegAlloc *ra_) const {
  // BoxLockNode is not a MachNode, so we can't just call MachNode::size(ra_).
  int offset = ra_->reg2offset(in_RegMask(0).find_first_elem());

  if (is_imm_in_range(offset, 12, 0)) {
    return NativeInstruction::instruction_size;
  } else {
    return 3 * NativeInstruction::instruction_size; // lui + addiw + add;
  }
}

//=============================================================================

#ifndef PRODUCT
void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const
{
  assert_cond(st != NULL);
  st->print_cr("# MachUEPNode");
  if (UseCompressedClassPointers) {
    st->print_cr("\tlwu t0, [j_rarg0, oopDesc::klass_offset_in_bytes()]\t# compressed klass");
    if (CompressedKlassPointers::shift() != 0) {
      st->print_cr("\tdecode_klass_not_null t0, t0");
    }
  } else {
    st->print_cr("\tld t0, [j_rarg0, oopDesc::klass_offset_in_bytes()]\t# compressed klass");
  }
  st->print_cr("\tbeq t0, t1, ic_hit");
  st->print_cr("\tj, SharedRuntime::_ic_miss_stub\t # Inline cache check");
  st->print_cr("\tic_hit:");
}
#endif

void MachUEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const
{
  // This is the unverified entry point.
  C2_MacroAssembler _masm(&cbuf);

  Label skip;
  __ cmp_klass(j_rarg0, t1, t0, t2 /* call-clobbered t2 as a tmp */, skip);
  __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub()));
  __ bind(skip);

  // These NOPs are critical so that verified entry point is properly
  // 4 bytes aligned for patching by NativeJump::patch_verified_entry()
  __ align(NativeInstruction::instruction_size);
}

uint MachUEPNode::size(PhaseRegAlloc* ra_) const
{
  assert_cond(ra_ != NULL);
  return MachNode::size(ra_);
}

// REQUIRED EMIT CODE

//=============================================================================

// Emit exception handler code.
int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf)
{
  // la_patchable t0, #exception_blob_entry_point
  // jr (offset)t0
  // or
  // j #exception_blob_entry_point
  // Note that the code buffer's insts_mark is always relative to insts.
  // That's why we must use the macroassembler to generate a handler.
  C2_MacroAssembler _masm(&cbuf);
  address base = __ start_a_stub(size_exception_handler());
  if (base == NULL) {
    ciEnv::current()->record_failure("CodeCache is full");
    return 0;  // CodeBuffer::expand failed
  }
  int offset = __ offset();
  __ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point()));
  assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
  __ end_a_stub();
  return offset;
}

// Emit deopt handler code.
int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf)
{
  // Note that the code buffer's insts_mark is always relative to insts.
  // That's why we must use the macroassembler to generate a handler.
  C2_MacroAssembler _masm(&cbuf);
  address base = __ start_a_stub(size_deopt_handler());
  if (base == NULL) {
    ciEnv::current()->record_failure("CodeCache is full");
    return 0;  // CodeBuffer::expand failed
  }
  int offset = __ offset();

  __ auipc(ra, 0);
  __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack()));

  assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow");
  __ end_a_stub();
  return offset;

}
// REQUIRED MATCHER CODE

//=============================================================================

const bool Matcher::match_rule_supported(int opcode) {
  if (!has_match_rule(opcode)) {
    return false;
  }

  switch (opcode) {
    case Op_CacheWB:           // fall through
    case Op_CacheWBPreSync:    // fall through
    case Op_CacheWBPostSync:
      if (!VM_Version::supports_data_cache_line_flush()) {
        return false;
      }
      break;

    case Op_StrCompressedCopy: // fall through
    case Op_StrInflatedCopy:   // fall through
    case Op_CountPositives:
      return UseRVV;

    case Op_EncodeISOArray:
      return UseRVV && SpecialEncodeISOArray;

    case Op_PopCountI:
    case Op_PopCountL:
      return UsePopCountInstruction;

    case Op_RotateRight:
    case Op_RotateLeft:
    case Op_CountLeadingZerosI:
    case Op_CountLeadingZerosL:
    case Op_CountTrailingZerosI:
    case Op_CountTrailingZerosL:
      return UseZbb;
  }

  return true; // Per default match rules are supported.
}

const bool Matcher::match_rule_supported_superword(int opcode, int vlen, BasicType bt) {
  return match_rule_supported_vector(opcode, vlen, bt);
}

// Identify extra cases that we might want to provide match rules for vector nodes and
// other intrinsics guarded with vector length (vlen) and element type (bt).
const bool Matcher::match_rule_supported_vector(int opcode, int vlen, BasicType bt) {
  if (!match_rule_supported(opcode) || !vector_size_supported(bt, vlen)) {
    return false;
  }

  return op_vec_supported(opcode);
}

const bool Matcher::match_rule_supported_vector_masked(int opcode, int vlen, BasicType bt) {
  return false;
}

const bool Matcher::vector_needs_partial_operations(Node* node, const TypeVect* vt) {
  return false;
}

const RegMask* Matcher::predicate_reg_mask(void) {
  return NULL;
}

const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) {
  return NULL;
}

// Vector calling convention not yet implemented.
const bool Matcher::supports_vector_calling_convention(void) {
  return false;
}

OptoRegPair Matcher::vector_return_value(uint ideal_reg) {
  Unimplemented();
  return OptoRegPair(0, 0);
}

// Is this branch offset short enough that a short branch can be used?
//
// NOTE: If the platform does not provide any short branch variants, then
//       this method should return false for offset 0.
// |---label(L1)-----|
// |-----------------|
// |-----------------|----------eq: float-------------------
// |-----------------| // far_cmpD_branch   |   cmpD_branch
// |------- ---------|    feq;              |      feq;
// |-far_cmpD_branch-|    beqz done;        |      bnez L;
// |-----------------|    j L;              |
// |-----------------|    bind(done);       |
// |-----------------|--------------------------------------
// |-----------------| // so shortBrSize = br_size - 4;
// |-----------------| // so offs = offset - shortBrSize + 4;
// |---label(L2)-----|
bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) {
  // The passed offset is relative to address of the branch.
  int shortBrSize = br_size - 4;
  int offs = offset - shortBrSize + 4;
  return (-4096 <= offs && offs < 4096);
}

// Vector width in bytes.
const int Matcher::vector_width_in_bytes(BasicType bt) {
  if (UseRVV) {
    // The MaxVectorSize should have been set by detecting RVV max vector register size when check UseRVV.
    // MaxVectorSize == VM_Version::_initial_vector_length
    return MaxVectorSize;
  }
  return 0;
}

// Limits on vector size (number of elements) loaded into vector.
const int Matcher::max_vector_size(const BasicType bt) {
  return vector_width_in_bytes(bt) / type2aelembytes(bt);
}
const int Matcher::min_vector_size(const BasicType bt) {
  return max_vector_size(bt);
}

// Vector ideal reg.
const uint Matcher::vector_ideal_reg(int len) {
  assert(MaxVectorSize >= len, "");
  if (UseRVV) {
    return Op_VecA;
  }

  ShouldNotReachHere();
  return 0;
}

const int Matcher::scalable_vector_reg_size(const BasicType bt) {
  return Matcher::max_vector_size(bt);
}

MachOper* Matcher::pd_specialize_generic_vector_operand(MachOper* original_opnd, uint ideal_reg, bool is_temp) {
  ShouldNotReachHere(); // generic vector operands not supported
  return NULL;
}

bool Matcher::is_reg2reg_move(MachNode* m) {
  ShouldNotReachHere(); // generic vector operands not supported
  return false;
}

bool Matcher::is_generic_vector(MachOper* opnd) {
  ShouldNotReachHere(); // generic vector operands not supported
  return false;
}

// Return whether or not this register is ever used as an argument.
// This function is used on startup to build the trampoline stubs in
// generateOptoStub.  Registers not mentioned will be killed by the VM
// call in the trampoline, and arguments in those registers not be
// available to the callee.
bool Matcher::can_be_java_arg(int reg)
{
  return
    reg ==  R10_num || reg == R10_H_num ||
    reg ==  R11_num || reg == R11_H_num ||
    reg ==  R12_num || reg == R12_H_num ||
    reg ==  R13_num || reg == R13_H_num ||
    reg ==  R14_num || reg == R14_H_num ||
    reg ==  R15_num || reg == R15_H_num ||
    reg ==  R16_num || reg == R16_H_num ||
    reg ==  R17_num || reg == R17_H_num ||
    reg ==  F10_num || reg == F10_H_num ||
    reg ==  F11_num || reg == F11_H_num ||
    reg ==  F12_num || reg == F12_H_num ||
    reg ==  F13_num || reg == F13_H_num ||
    reg ==  F14_num || reg == F14_H_num ||
    reg ==  F15_num || reg == F15_H_num ||
    reg ==  F16_num || reg == F16_H_num ||
    reg ==  F17_num || reg == F17_H_num;
}

bool Matcher::is_spillable_arg(int reg)
{
  return can_be_java_arg(reg);
}

uint Matcher::int_pressure_limit()
{
  // A derived pointer is live at CallNode and then is flagged by RA
  // as a spilled LRG. Spilling heuristics(Spill-USE) explicitly skip
  // derived pointers and lastly fail to spill after reaching maximum
  // number of iterations. Lowering the default pressure threshold to
  // (_NO_SPECIAL_REG32_mask.Size() minus 1) forces CallNode to become
  // a high register pressure area of the code so that split_DEF can
  // generate DefinitionSpillCopy for the derived pointer.
  uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.Size() - 1;
  if (!PreserveFramePointer) {
    // When PreserveFramePointer is off, frame pointer is allocatable,
    // but different from other SOC registers, it is excluded from
    // fatproj's mask because its save type is No-Save. Decrease 1 to
    // ensure high pressure at fatproj when PreserveFramePointer is off.
    // See check_pressure_at_fatproj().
    default_int_pressure_threshold--;
  }
  return (INTPRESSURE == -1) ? default_int_pressure_threshold : INTPRESSURE;
}

uint Matcher::float_pressure_limit()
{
  // _FLOAT_REG_mask is generated by adlc from the float_reg register class.
  return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.Size() : FLOATPRESSURE;
}

bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) {
  return false;
}

RegMask Matcher::divI_proj_mask() {
  ShouldNotReachHere();
  return RegMask();
}

// Register for MODI projection of divmodI.
RegMask Matcher::modI_proj_mask() {
  ShouldNotReachHere();
  return RegMask();
}

// Register for DIVL projection of divmodL.
RegMask Matcher::divL_proj_mask() {
  ShouldNotReachHere();
  return RegMask();
}

// Register for MODL projection of divmodL.
RegMask Matcher::modL_proj_mask() {
  ShouldNotReachHere();
  return RegMask();
}

const RegMask Matcher::method_handle_invoke_SP_save_mask() {
  return FP_REG_mask();
}

bool size_fits_all_mem_uses(AddPNode* addp, int shift) {
  assert_cond(addp != NULL);
  for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) {
    Node* u = addp->fast_out(i);
    if (u != NULL && u->is_Mem()) {
      int opsize = u->as_Mem()->memory_size();
      assert(opsize > 0, "unexpected memory operand size");
      if (u->as_Mem()->memory_size() != (1 << shift)) {
        return false;
      }
    }
  }
  return true;
}

// Should the Matcher clone input 'm' of node 'n'?
bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) {
  assert_cond(m != NULL);
  if (is_vshift_con_pattern(n, m)) { // ShiftV src (ShiftCntV con)
    mstack.push(m, Visit);           // m = ShiftCntV
    return true;
  }
  return false;
}

// Should the Matcher clone shifts on addressing modes, expecting them
// to be subsumed into complex addressing expressions or compute them
// into registers?
bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, VectorSet& address_visited) {
  return clone_base_plus_offset_address(m, mstack, address_visited);
}

%}



//----------ENCODING BLOCK-----------------------------------------------------
// This block specifies the encoding classes used by the compiler to
// output byte streams.  Encoding classes are parameterized macros
// used by Machine Instruction Nodes in order to generate the bit
// encoding of the instruction.  Operands specify their base encoding
// interface with the interface keyword.  There are currently
// supported four interfaces, REG_INTER, CONST_INTER, MEMORY_INTER, &
// COND_INTER.  REG_INTER causes an operand to generate a function
// which returns its register number when queried.  CONST_INTER causes
// an operand to generate a function which returns the value of the
// constant when queried.  MEMORY_INTER causes an operand to generate
// four functions which return the Base Register, the Index Register,
// the Scale Value, and the Offset Value of the operand when queried.
// COND_INTER causes an operand to generate six functions which return
// the encoding code (ie - encoding bits for the instruction)
// associated with each basic boolean condition for a conditional
// instruction.
//
// Instructions specify two basic values for encoding.  Again, a
// function is available to check if the constant displacement is an
// oop. They use the ins_encode keyword to specify their encoding
// classes (which must be a sequence of enc_class names, and their
// parameters, specified in the encoding block), and they use the
// opcode keyword to specify, in order, their primary, secondary, and
// tertiary opcode.  Only the opcode sections which a particular
// instruction needs for encoding need to be specified.
encode %{
  // BEGIN Non-volatile memory access

  enc_class riscv_enc_li_imm(iRegIorL dst, immIorL src) %{
    C2_MacroAssembler _masm(&cbuf);
    int64_t con = (int64_t)$src$$constant;
    Register dst_reg = as_Register($dst$$reg);
    __ mv(dst_reg, con);
  %}

  enc_class riscv_enc_mov_p(iRegP dst, immP src) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    address con = (address)$src$$constant;
    if (con == NULL || con == (address)1) {
      ShouldNotReachHere();
    } else {
      relocInfo::relocType rtype = $src->constant_reloc();
      if (rtype == relocInfo::oop_type) {
        __ movoop(dst_reg, (jobject)con);
      } else if (rtype == relocInfo::metadata_type) {
        __ mov_metadata(dst_reg, (Metadata*)con);
      } else {
        assert(rtype == relocInfo::none, "unexpected reloc type");
        __ mv(dst_reg, $src$$constant);
      }
    }
  %}

  enc_class riscv_enc_mov_p1(iRegP dst) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    __ mv(dst_reg, 1);
  %}

  enc_class riscv_enc_mov_byte_map_base(iRegP dst) %{
    C2_MacroAssembler _masm(&cbuf);
    __ load_byte_map_base($dst$$Register);
  %}

  enc_class riscv_enc_mov_n(iRegN dst, immN src) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    address con = (address)$src$$constant;
    if (con == NULL) {
      ShouldNotReachHere();
    } else {
      relocInfo::relocType rtype = $src->constant_reloc();
      assert(rtype == relocInfo::oop_type, "unexpected reloc type");
      __ set_narrow_oop(dst_reg, (jobject)con);
    }
  %}

  enc_class riscv_enc_mov_zero(iRegNorP dst) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    __ mv(dst_reg, zr);
  %}

  enc_class riscv_enc_mov_nk(iRegN dst, immNKlass src) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    address con = (address)$src$$constant;
    if (con == NULL) {
      ShouldNotReachHere();
    } else {
      relocInfo::relocType rtype = $src->constant_reloc();
      assert(rtype == relocInfo::metadata_type, "unexpected reloc type");
      __ set_narrow_klass(dst_reg, (Klass *)con);
    }
  %}

  enc_class riscv_enc_cmpxchgw(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  enc_class riscv_enc_cmpxchgn(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  enc_class riscv_enc_cmpxchg(iRegINoSp res, memory mem, iRegL oldval, iRegL newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  enc_class riscv_enc_cmpxchgw_acq(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  enc_class riscv_enc_cmpxchgn_acq(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  enc_class riscv_enc_cmpxchg_acq(iRegINoSp res, memory mem, iRegL oldval, iRegL newval) %{
    C2_MacroAssembler _masm(&cbuf);
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
               /*result as bool*/ true);
  %}

  // compare and branch instruction encodings

  enc_class riscv_enc_j(label lbl) %{
    C2_MacroAssembler _masm(&cbuf);
    Label* L = $lbl$$label;
    __ j(*L);
  %}

  enc_class riscv_enc_far_cmpULtGe_imm0_branch(cmpOpULtGe cmp, iRegIorL op1, label lbl) %{
    C2_MacroAssembler _masm(&cbuf);
    Label* L = $lbl$$label;
    switch ($cmp$$cmpcode) {
      case(BoolTest::ge):
        __ j(*L);
        break;
      case(BoolTest::lt):
        break;
      default:
        Unimplemented();
    }
  %}

  // call instruction encodings

  enc_class riscv_enc_partial_subtype_check(iRegP sub, iRegP super, iRegP temp, iRegP result) %{
    Register sub_reg = as_Register($sub$$reg);
    Register super_reg = as_Register($super$$reg);
    Register temp_reg = as_Register($temp$$reg);
    Register result_reg = as_Register($result$$reg);
    Register cr_reg = t1;

    Label miss;
    Label done;
    C2_MacroAssembler _masm(&cbuf);
    __ check_klass_subtype_slow_path(sub_reg, super_reg, temp_reg, result_reg,
                                     NULL, &miss);
    if ($primary) {
      __ mv(result_reg, zr);
    } else {
      __ mv(cr_reg, zr);
      __ j(done);
    }

    __ bind(miss);
    if (!$primary) {
      __ mv(cr_reg, 1);
    }

    __ bind(done);
  %}

  enc_class riscv_enc_java_static_call(method meth) %{
    C2_MacroAssembler _masm(&cbuf);
    Assembler::IncompressibleRegion ir(&_masm);  // Fixed length: see ret_addr_offset

    address addr = (address)$meth$$method;
    address call = NULL;
    assert_cond(addr != NULL);
    if (!_method) {
      // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap.
      call = __ trampoline_call(Address(addr, relocInfo::runtime_call_type));
      if (call == NULL) {
        ciEnv::current()->record_failure("CodeCache is full");
        return;
      }
    } else {
      int method_index = resolved_method_index(cbuf);
      RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
                                                  : static_call_Relocation::spec(method_index);
      call = __ trampoline_call(Address(addr, rspec));
      if (call == NULL) {
        ciEnv::current()->record_failure("CodeCache is full");
        return;
      }

      if (CodeBuffer::supports_shared_stubs() && _method->can_be_statically_bound()) {
        // Calls of the same statically bound method can share
        // a stub to the interpreter.
        cbuf.shared_stub_to_interp_for(_method, call - cbuf.insts_begin());
      } else {
        // Emit stub for static call
        address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, call);
        if (stub == NULL) {
          ciEnv::current()->record_failure("CodeCache is full");
          return;
        }
      }
    }

    __ post_call_nop();
  %}

  enc_class riscv_enc_java_dynamic_call(method meth) %{
    C2_MacroAssembler _masm(&cbuf);
    Assembler::IncompressibleRegion ir(&_masm);  // Fixed length: see ret_addr_offset
    int method_index = resolved_method_index(cbuf);
    address call = __ ic_call((address)$meth$$method, method_index);
    if (call == NULL) {
      ciEnv::current()->record_failure("CodeCache is full");
      return;
    }

    __ post_call_nop();
  %}

  enc_class riscv_enc_call_epilog() %{
    C2_MacroAssembler _masm(&cbuf);
    if (VerifyStackAtCalls) {
      // Check that stack depth is unchanged: find majik cookie on stack
      __ call_Unimplemented();
    }
  %}

  enc_class riscv_enc_java_to_runtime(method meth) %{
    C2_MacroAssembler _masm(&cbuf);
    Assembler::IncompressibleRegion ir(&_masm);  // Fixed length: see ret_addr_offset

    // some calls to generated routines (arraycopy code) are scheduled
    // by C2 as runtime calls. if so we can call them using a jr (they
    // will be in a reachable segment) otherwise we have to use a jalr
    // which loads the absolute address into a register.
    address entry = (address)$meth$$method;
    CodeBlob *cb = CodeCache::find_blob(entry);
    if (cb != NULL) {
      address call = __ trampoline_call(Address(entry, relocInfo::runtime_call_type));
      if (call == NULL) {
        ciEnv::current()->record_failure("CodeCache is full");
        return;
      }
      __ post_call_nop();
    } else {
      Label retaddr;
      __ la(t1, retaddr);
      __ la(t0, RuntimeAddress(entry));
      // Leave a breadcrumb for JavaFrameAnchor::capture_last_Java_pc()
      __ addi(sp, sp, -2 * wordSize);
      __ sd(t1, Address(sp, wordSize));
      __ jalr(t0);
      __ bind(retaddr);
      __ post_call_nop();
      __ addi(sp, sp, 2 * wordSize);
    }
  %}

  // using the cr register as the bool result: 0 for success; others failed.
  enc_class riscv_enc_fast_lock(iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register flag = t1;
    Register oop = as_Register($object$$reg);
    Register box = as_Register($box$$reg);
    Register disp_hdr = as_Register($tmp1$$reg);
    Register tmp = as_Register($tmp2$$reg);
    Label cont;
    Label object_has_monitor;
    Label no_count;

    assert_different_registers(oop, box, tmp, disp_hdr, t0);

    // Load markWord from object into displaced_header.
    __ ld(disp_hdr, Address(oop, oopDesc::mark_offset_in_bytes()));

    if (DiagnoseSyncOnValueBasedClasses != 0) {
      __ load_klass(flag, oop);
      __ lwu(flag, Address(flag, Klass::access_flags_offset()));
      __ andi(flag, flag, JVM_ACC_IS_VALUE_BASED_CLASS, tmp /* tmp */);
      __ bnez(flag, cont, true /* is_far */);
    }

    // Check for existing monitor
    __ andi(t0, disp_hdr, markWord::monitor_value);
    __ bnez(t0, object_has_monitor);

    if (!UseHeavyMonitors) {
      // Set tmp to be (markWord of object | UNLOCK_VALUE).
      __ ori(tmp, disp_hdr, markWord::unlocked_value);

      // Initialize the box. (Must happen before we update the object mark!)
      __ sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes()));

      // Compare object markWord with an unlocked value (tmp) and if
      // equal exchange the stack address of our box with object markWord.
      // On failure disp_hdr contains the possibly locked markWord.
      __ cmpxchg(/*memory address*/oop, /*expected value*/tmp, /*new value*/box, Assembler::int64, Assembler::aq,
                 Assembler::rl, /*result*/disp_hdr);
      __ mv(flag, zr);
      __ beq(disp_hdr, tmp, cont); // prepare zero flag and goto cont if we won the cas

      assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0");

      // If the compare-and-exchange succeeded, then we found an unlocked
      // object, will have now locked it will continue at label cont
      // We did not see an unlocked object so try the fast recursive case.

      // Check if the owner is self by comparing the value in the
      // markWord of object (disp_hdr) with the stack pointer.
      __ sub(disp_hdr, disp_hdr, sp);
      __ mv(tmp, (intptr_t) (~(os::vm_page_size()-1) | (uintptr_t)markWord::lock_mask_in_place));
      // If (mark & lock_mask) == 0 and mark - sp < page_size, we are stack-locking and goto cont,
      // hence we can store 0 as the displaced header in the box, which indicates that it is a
      // recursive lock.
      __ andr(tmp/*==0?*/, disp_hdr, tmp);
      __ sd(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes()));
      __ mv(flag, tmp); // we can use the value of tmp as the result here
    } else {
      __ mv(flag, 1); // Set non-zero flag to indicate 'failure' -> take slow-path
    }

    __ j(cont);

    // Handle existing monitor.
    __ bind(object_has_monitor);
    // The object's monitor m is unlocked iff m->owner == NULL,
    // otherwise m->owner may contain a thread or a stack address.
    //
    // Try to CAS m->owner from NULL to current thread.
    __ add(tmp, disp_hdr, (ObjectMonitor::owner_offset_in_bytes() - markWord::monitor_value));
    __ cmpxchg(/*memory address*/tmp, /*expected value*/zr, /*new value*/xthread, Assembler::int64, Assembler::aq,
             Assembler::rl, /*result*/flag); // cas succeeds if flag == zr(expected)

    // Store a non-null value into the box to avoid looking like a re-entrant
    // lock. The fast-path monitor unlock code checks for
    // markWord::monitor_value so use markWord::unused_mark which has the
    // relevant bit set, and also matches ObjectSynchronizer::slow_enter.
    __ mv(tmp, (address)markWord::unused_mark().value());
    __ sd(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes()));

    __ beqz(flag, cont); // CAS success means locking succeeded

    __ bne(flag, xthread, cont); // Check for recursive locking

    // Recursive lock case
    __ mv(flag, zr);
    __ increment(Address(disp_hdr, ObjectMonitor::recursions_offset_in_bytes() - markWord::monitor_value), 1, t0, tmp);

    __ bind(cont);

    __ bnez(flag, no_count);

    __ increment(Address(xthread, JavaThread::held_monitor_count_offset()), 1, t0, tmp);

    __ bind(no_count);
  %}

  // using cr flag to indicate the fast_unlock result: 0 for success; others failed.
  enc_class riscv_enc_fast_unlock(iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register flag = t1;
    Register oop = as_Register($object$$reg);
    Register box = as_Register($box$$reg);
    Register disp_hdr = as_Register($tmp1$$reg);
    Register tmp = as_Register($tmp2$$reg);
    Label cont;
    Label object_has_monitor;
    Label no_count;

    assert_different_registers(oop, box, tmp, disp_hdr, flag);

    if (!UseHeavyMonitors) {
      // Find the lock address and load the displaced header from the stack.
      __ ld(disp_hdr, Address(box, BasicLock::displaced_header_offset_in_bytes()));

      // If the displaced header is 0, we have a recursive unlock.
      __ mv(flag, disp_hdr);
      __ beqz(disp_hdr, cont);
    }

    // Handle existing monitor.
    __ ld(tmp, Address(oop, oopDesc::mark_offset_in_bytes()));
    __ andi(t0, tmp, markWord::monitor_value);
    __ bnez(t0, object_has_monitor);

    if (!UseHeavyMonitors) {
      // Check if it is still a light weight lock, this is true if we
      // see the stack address of the basicLock in the markWord of the
      // object.

      __ cmpxchg(/*memory address*/oop, /*expected value*/box, /*new value*/disp_hdr, Assembler::int64, Assembler::relaxed,
                 Assembler::rl, /*result*/tmp);
      __ xorr(flag, box, tmp); // box == tmp if cas succeeds
    } else {
      __ mv(flag, 1); // Set non-zero flag to indicate 'failure' -> take slow path
    }
    __ j(cont);

    assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0");

    // Handle existing monitor.
    __ bind(object_has_monitor);
    STATIC_ASSERT(markWord::monitor_value <= INT_MAX);
    __ add(tmp, tmp, -(int)markWord::monitor_value); // monitor
    __ ld(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes()));

    Label notRecursive;
    __ beqz(disp_hdr, notRecursive); // Will be 0 if not recursive.

    // Recursive lock
    __ addi(disp_hdr, disp_hdr, -1);
    __ sd(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes()));
    __ mv(flag, zr);
    __ j(cont);

    __ bind(notRecursive);
    __ ld(flag, Address(tmp, ObjectMonitor::EntryList_offset_in_bytes()));
    __ ld(disp_hdr, Address(tmp, ObjectMonitor::cxq_offset_in_bytes()));
    __ orr(flag, flag, disp_hdr); // Will be 0 if both are 0.
    __ bnez(flag, cont);
    // need a release store here
    __ la(tmp, Address(tmp, ObjectMonitor::owner_offset_in_bytes()));
    __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
    __ sd(zr, Address(tmp)); // set unowned

    __ bind(cont);

    __ bnez(flag, no_count);

    __ decrement(Address(xthread, JavaThread::held_monitor_count_offset()), 1, t0, tmp);

    __ bind(no_count);
  %}

  // arithmetic encodings

  enc_class riscv_enc_divw(iRegI dst, iRegI src1, iRegI src2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    Register src1_reg = as_Register($src1$$reg);
    Register src2_reg = as_Register($src2$$reg);
    __ corrected_idivl(dst_reg, src1_reg, src2_reg, false);
  %}

  enc_class riscv_enc_div(iRegI dst, iRegI src1, iRegI src2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    Register src1_reg = as_Register($src1$$reg);
    Register src2_reg = as_Register($src2$$reg);
    __ corrected_idivq(dst_reg, src1_reg, src2_reg, false);
  %}

  enc_class riscv_enc_modw(iRegI dst, iRegI src1, iRegI src2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    Register src1_reg = as_Register($src1$$reg);
    Register src2_reg = as_Register($src2$$reg);
    __ corrected_idivl(dst_reg, src1_reg, src2_reg, true);
  %}

  enc_class riscv_enc_mod(iRegI dst, iRegI src1, iRegI src2) %{
    C2_MacroAssembler _masm(&cbuf);
    Register dst_reg = as_Register($dst$$reg);
    Register src1_reg = as_Register($src1$$reg);
    Register src2_reg = as_Register($src2$$reg);
    __ corrected_idivq(dst_reg, src1_reg, src2_reg, true);
  %}

  enc_class riscv_enc_tail_call(iRegP jump_target) %{
    C2_MacroAssembler _masm(&cbuf);
    Register target_reg = as_Register($jump_target$$reg);
    __ jr(target_reg);
  %}

  enc_class riscv_enc_tail_jmp(iRegP jump_target) %{
    C2_MacroAssembler _masm(&cbuf);
    Register target_reg = as_Register($jump_target$$reg);
    // exception oop should be in x10
    // ret addr has been popped into ra
    // callee expects it in x13
    __ mv(x13, ra);
    __ jr(target_reg);
  %}

  enc_class riscv_enc_rethrow() %{
    C2_MacroAssembler _masm(&cbuf);
    __ far_jump(RuntimeAddress(OptoRuntime::rethrow_stub()));
  %}

  enc_class riscv_enc_ret() %{
    C2_MacroAssembler _masm(&cbuf);
    __ ret();
  %}

%}

//----------FRAME--------------------------------------------------------------
// Definition of frame structure and management information.
//
//  S T A C K   L A Y O U T    Allocators stack-slot number
//                             |   (to get allocators register number
//  G  Owned by    |        |  v    add OptoReg::stack0())
//  r   CALLER     |        |
//  o     |        +--------+      pad to even-align allocators stack-slot
//  w     V        |  pad0  |        numbers; owned by CALLER
//  t   -----------+--------+----> Matcher::_in_arg_limit, unaligned
//  h     ^        |   in   |  5
//        |        |  args  |  4   Holes in incoming args owned by SELF
//  |     |        |        |  3
//  |     |        +--------+
//  V     |        | old out|      Empty on Intel, window on Sparc
//        |    old |preserve|      Must be even aligned.
//        |     SP-+--------+----> Matcher::_old_SP, even aligned
//        |        |   in   |  3   area for Intel ret address
//     Owned by    |preserve|      Empty on Sparc.
//       SELF      +--------+
//        |        |  pad2  |  2   pad to align old SP
//        |        +--------+  1
//        |        | locks  |  0
//        |        +--------+----> OptoReg::stack0(), even aligned
//        |        |  pad1  | 11   pad to align new SP
//        |        +--------+
//        |        |        | 10
//        |        | spills |  9   spills
//        V        |        |  8   (pad0 slot for callee)
//      -----------+--------+----> Matcher::_out_arg_limit, unaligned
//        ^        |  out   |  7
//        |        |  args  |  6   Holes in outgoing args owned by CALLEE
//     Owned by    +--------+
//      CALLEE     | new out|  6   Empty on Intel, window on Sparc
//        |    new |preserve|      Must be even-aligned.
//        |     SP-+--------+----> Matcher::_new_SP, even aligned
//        |        |        |
//
// Note 1: Only region 8-11 is determined by the allocator.  Region 0-5 is
//         known from SELF's arguments and the Java calling convention.
//         Region 6-7 is determined per call site.
// Note 2: If the calling convention leaves holes in the incoming argument
//         area, those holes are owned by SELF.  Holes in the outgoing area
//         are owned by the CALLEE.  Holes should not be necessary in the
//         incoming area, as the Java calling convention is completely under
//         the control of the AD file.  Doubles can be sorted and packed to
//         avoid holes.  Holes in the outgoing arguments may be necessary for
//         varargs C calling conventions.
// Note 3: Region 0-3 is even aligned, with pad2 as needed.  Region 3-5 is
//         even aligned with pad0 as needed.
//         Region 6 is even aligned.  Region 6-7 is NOT even aligned;
//           (the latter is true on Intel but is it false on RISCV?)
//         region 6-11 is even aligned; it may be padded out more so that
//         the region from SP to FP meets the minimum stack alignment.
// Note 4: For I2C adapters, the incoming FP may not meet the minimum stack
//         alignment.  Region 11, pad1, may be dynamically extended so that
//         SP meets the minimum alignment.

frame %{
  // These three registers define part of the calling convention
  // between compiled code and the interpreter.

  // Inline Cache Register or methodOop for I2C.
  inline_cache_reg(R31);

  // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset]
  cisc_spilling_operand_name(indOffset);

  // Number of stack slots consumed by locking an object
  // generate Compile::sync_stack_slots
  // VMRegImpl::slots_per_word = wordSize / stack_slot_size = 8 / 4 = 2
  sync_stack_slots(1 * VMRegImpl::slots_per_word);

  // Compiled code's Frame Pointer
  frame_pointer(R2);

  // Interpreter stores its frame pointer in a register which is
  // stored to the stack by I2CAdaptors.
  // I2CAdaptors convert from interpreted java to compiled java.
  interpreter_frame_pointer(R8);

  // Stack alignment requirement
  stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes)

  // Number of outgoing stack slots killed above the out_preserve_stack_slots
  // for calls to C.  Supports the var-args backing area for register parms.
  varargs_C_out_slots_killed(frame::arg_reg_save_area_bytes / BytesPerInt);

  // The after-PROLOG location of the return address.  Location of
  // return address specifies a type (REG or STACK) and a number
  // representing the register number (i.e. - use a register name) or
  // stack slot.
  // Ret Addr is on stack in slot 0 if no locks or verification or alignment.
  // Otherwise, it is above the locks and verification slot and alignment word
  // TODO this may well be correct but need to check why that - 2 is there
  // ppc port uses 0 but we definitely need to allow for fixed_slots
  // which folds in the space used for monitors
  return_addr(STACK - 2 +
              align_up((Compile::current()->in_preserve_stack_slots() +
                        Compile::current()->fixed_slots()),
                       stack_alignment_in_slots()));

  // Location of compiled Java return values.  Same as C for now.
  return_value
  %{
    assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL,
           "only return normal values");

    static const int lo[Op_RegL + 1] = { // enum name
      0,                                 // Op_Node
      0,                                 // Op_Set
      R10_num,                           // Op_RegN
      R10_num,                           // Op_RegI
      R10_num,                           // Op_RegP
      F10_num,                           // Op_RegF
      F10_num,                           // Op_RegD
      R10_num                            // Op_RegL
    };

    static const int hi[Op_RegL + 1] = { // enum name
      0,                                 // Op_Node
      0,                                 // Op_Set
      OptoReg::Bad,                      // Op_RegN
      OptoReg::Bad,                      // Op_RegI
      R10_H_num,                         // Op_RegP
      OptoReg::Bad,                      // Op_RegF
      F10_H_num,                         // Op_RegD
      R10_H_num                          // Op_RegL
    };

    return OptoRegPair(hi[ideal_reg], lo[ideal_reg]);
  %}
%}

//----------ATTRIBUTES---------------------------------------------------------
//----------Operand Attributes-------------------------------------------------
op_attrib op_cost(1);        // Required cost attribute

//----------Instruction Attributes---------------------------------------------
ins_attrib ins_cost(DEFAULT_COST); // Required cost attribute
ins_attrib ins_size(32);        // Required size attribute (in bits)
ins_attrib ins_short_branch(0); // Required flag: is this instruction
                                // a non-matching short branch variant
                                // of some long branch?
ins_attrib ins_alignment(4);    // Required alignment attribute (must
                                // be a power of 2) specifies the
                                // alignment that some part of the
                                // instruction (not necessarily the
                                // start) requires.  If > 1, a
                                // compute_padding() function must be
                                // provided for the instruction

//----------OPERANDS-----------------------------------------------------------
// Operand definitions must precede instruction definitions for correct parsing
// in the ADLC because operands constitute user defined types which are used in
// instruction definitions.

//----------Simple Operands----------------------------------------------------

// Integer operands 32 bit
// 32 bit immediate
operand immI()
%{
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 32 bit zero
operand immI0()
%{
  predicate(n->get_int() == 0);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 32 bit unit increment
operand immI_1()
%{
  predicate(n->get_int() == 1);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 32 bit unit decrement
operand immI_M1()
%{
  predicate(n->get_int() == -1);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Unsigned Integer Immediate:  6-bit int, greater than 32
operand uimmI6_ge32() %{
  predicate(((unsigned int)(n->get_int()) < 64) && (n->get_int() >= 32));
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immI_le_4()
%{
  predicate(n->get_int() <= 4);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immI_16()
%{
  predicate(n->get_int() == 16);
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immI_24()
%{
  predicate(n->get_int() == 24);
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immI_31()
%{
  predicate(n->get_int() == 31);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immI_63()
%{
  predicate(n->get_int() == 63);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 32 bit integer valid for add immediate
operand immIAdd()
%{
  predicate(Assembler::operand_valid_for_add_immediate((int64_t)n->get_int()));
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 32 bit integer valid for sub immediate
operand immISub()
%{
  predicate(Assembler::operand_valid_for_add_immediate(-(int64_t)n->get_int()));
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 5 bit signed value.
operand immI5()
%{
  predicate(n->get_int() <= 15 && n->get_int() >= -16);
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 5 bit signed value (simm5)
operand immL5()
%{
  predicate(n->get_long() <= 15 && n->get_long() >= -16);
  match(ConL);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Integer operands 64 bit
// 64 bit immediate
operand immL()
%{
  match(ConL);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 64 bit zero
operand immL0()
%{
  predicate(n->get_long() == 0);
  match(ConL);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Pointer operands
// Pointer Immediate
operand immP()
%{
  match(ConP);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// NULL Pointer Immediate
operand immP0()
%{
  predicate(n->get_ptr() == 0);
  match(ConP);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Pointer Immediate One
// this is used in object initialization (initial object header)
operand immP_1()
%{
  predicate(n->get_ptr() == 1);
  match(ConP);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Card Table Byte Map Base
operand immByteMapBase()
%{
  // Get base of card map
  predicate(BarrierSet::barrier_set()->is_a(BarrierSet::CardTableBarrierSet) &&
            (CardTable::CardValue*)n->get_ptr() ==
             ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base());
  match(ConP);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Int Immediate: low 16-bit mask
operand immI_16bits()
%{
  predicate(n->get_int() == 0xFFFF);
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immIpowerOf2() %{
  predicate(is_power_of_2((juint)(n->get_int())));
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Long Immediate: low 32-bit mask
operand immL_32bits()
%{
  predicate(n->get_long() == 0xFFFFFFFFL);
  match(ConL);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 64 bit unit decrement
operand immL_M1()
%{
  predicate(n->get_long() == -1);
  match(ConL);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}


// 32 bit offset of pc in thread anchor

operand immL_pc_off()
%{
  predicate(n->get_long() == in_bytes(JavaThread::frame_anchor_offset()) +
                             in_bytes(JavaFrameAnchor::last_Java_pc_offset()));
  match(ConL);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 64 bit integer valid for add immediate
operand immLAdd()
%{
  predicate(Assembler::operand_valid_for_add_immediate(n->get_long()));
  match(ConL);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// 64 bit integer valid for sub immediate
operand immLSub()
%{
  predicate(Assembler::operand_valid_for_add_immediate(-(n->get_long())));
  match(ConL);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Narrow pointer operands
// Narrow Pointer Immediate
operand immN()
%{
  match(ConN);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Narrow NULL Pointer Immediate
operand immN0()
%{
  predicate(n->get_narrowcon() == 0);
  match(ConN);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immNKlass()
%{
  match(ConNKlass);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Float and Double operands
// Double Immediate
operand immD()
%{
  match(ConD);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Double Immediate: +0.0d
operand immD0()
%{
  predicate(jlong_cast(n->getd()) == 0);
  match(ConD);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Float Immediate
operand immF()
%{
  match(ConF);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Float Immediate: +0.0f.
operand immF0()
%{
  predicate(jint_cast(n->getf()) == 0);
  match(ConF);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immIOffset()
%{
  predicate(is_imm_in_range(n->get_int(), 12, 0));
  match(ConI);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

operand immLOffset()
%{
  predicate(is_imm_in_range(n->get_long(), 12, 0));
  match(ConL);
  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Scale values
operand immIScale()
%{
  predicate(1 <= n->get_int() && (n->get_int() <= 3));
  match(ConI);

  op_cost(0);
  format %{ %}
  interface(CONST_INTER);
%}

// Integer 32 bit Register Operands
operand iRegI()
%{
  constraint(ALLOC_IN_RC(any_reg32));
  match(RegI);
  match(iRegINoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Integer 32 bit Register not Special
operand iRegINoSp()
%{
  constraint(ALLOC_IN_RC(no_special_reg32));
  match(RegI);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Register R10 only
operand iRegI_R10()
%{
  constraint(ALLOC_IN_RC(int_r10_reg));
  match(RegI);
  match(iRegINoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Register R12 only
operand iRegI_R12()
%{
  constraint(ALLOC_IN_RC(int_r12_reg));
  match(RegI);
  match(iRegINoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Register R13 only
operand iRegI_R13()
%{
  constraint(ALLOC_IN_RC(int_r13_reg));
  match(RegI);
  match(iRegINoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Register R14 only
operand iRegI_R14()
%{
  constraint(ALLOC_IN_RC(int_r14_reg));
  match(RegI);
  match(iRegINoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Integer 64 bit Register Operands
operand iRegL()
%{
  constraint(ALLOC_IN_RC(any_reg));
  match(RegL);
  match(iRegLNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Integer 64 bit Register not Special
operand iRegLNoSp()
%{
  constraint(ALLOC_IN_RC(no_special_reg));
  match(RegL);
  match(iRegL_R10);
  format %{ %}
  interface(REG_INTER);
%}

// Long 64 bit Register R28 only
operand iRegL_R28()
%{
  constraint(ALLOC_IN_RC(r28_reg));
  match(RegL);
  match(iRegLNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Long 64 bit Register R29 only
operand iRegL_R29()
%{
  constraint(ALLOC_IN_RC(r29_reg));
  match(RegL);
  match(iRegLNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Long 64 bit Register R30 only
operand iRegL_R30()
%{
  constraint(ALLOC_IN_RC(r30_reg));
  match(RegL);
  match(iRegLNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer Register Operands
// Pointer Register
operand iRegP()
%{
  constraint(ALLOC_IN_RC(ptr_reg));
  match(RegP);
  match(iRegPNoSp);
  match(iRegP_R10);
  match(iRegP_R15);
  match(javaThread_RegP);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register not Special
operand iRegPNoSp()
%{
  constraint(ALLOC_IN_RC(no_special_ptr_reg));
  match(RegP);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand iRegP_R10()
%{
  constraint(ALLOC_IN_RC(r10_reg));
  match(RegP);
  // match(iRegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register R11 only
operand iRegP_R11()
%{
  constraint(ALLOC_IN_RC(r11_reg));
  match(RegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand iRegP_R12()
%{
  constraint(ALLOC_IN_RC(r12_reg));
  match(RegP);
  // match(iRegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register R13 only
operand iRegP_R13()
%{
  constraint(ALLOC_IN_RC(r13_reg));
  match(RegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand iRegP_R14()
%{
  constraint(ALLOC_IN_RC(r14_reg));
  match(RegP);
  // match(iRegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand iRegP_R15()
%{
  constraint(ALLOC_IN_RC(r15_reg));
  match(RegP);
  // match(iRegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand iRegP_R16()
%{
  constraint(ALLOC_IN_RC(r16_reg));
  match(RegP);
  // match(iRegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register R28 only
operand iRegP_R28()
%{
  constraint(ALLOC_IN_RC(r28_reg));
  match(RegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register R30 only
operand iRegP_R30()
%{
  constraint(ALLOC_IN_RC(r30_reg));
  match(RegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer 64 bit Register R31 only
operand iRegP_R31()
%{
  constraint(ALLOC_IN_RC(r31_reg));
  match(RegP);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Pointer Register Operands
// Narrow Pointer Register
operand iRegN()
%{
  constraint(ALLOC_IN_RC(any_reg32));
  match(RegN);
  match(iRegNNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Integer 64 bit Register not Special
operand iRegNNoSp()
%{
  constraint(ALLOC_IN_RC(no_special_reg32));
  match(RegN);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// heap base register -- used for encoding immN0
operand iRegIHeapbase()
%{
  constraint(ALLOC_IN_RC(heapbase_reg));
  match(RegI);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Long 64 bit Register R10 only
operand iRegL_R10()
%{
  constraint(ALLOC_IN_RC(r10_reg));
  match(RegL);
  match(iRegLNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Float Register
// Float register operands
operand fRegF()
%{
  constraint(ALLOC_IN_RC(float_reg));
  match(RegF);

  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Double Register
// Double register operands
operand fRegD()
%{
  constraint(ALLOC_IN_RC(double_reg));
  match(RegD);

  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Generic vector class. This will be used for
// all vector operands.
operand vReg()
%{
  constraint(ALLOC_IN_RC(vectora_reg));
  match(VecA);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand vReg_V1()
%{
  constraint(ALLOC_IN_RC(v1_reg));
  match(VecA);
  match(vReg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand vReg_V2()
%{
  constraint(ALLOC_IN_RC(v2_reg));
  match(VecA);
  match(vReg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand vReg_V3()
%{
  constraint(ALLOC_IN_RC(v3_reg));
  match(VecA);
  match(vReg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand vReg_V4()
%{
  constraint(ALLOC_IN_RC(v4_reg));
  match(VecA);
  match(vReg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

operand vReg_V5()
%{
  constraint(ALLOC_IN_RC(v5_reg));
  match(VecA);
  match(vReg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

// Java Thread Register
operand javaThread_RegP(iRegP reg)
%{
  constraint(ALLOC_IN_RC(java_thread_reg)); // java_thread_reg
  match(reg);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

//----------Memory Operands----------------------------------------------------
// RISCV has only base_plus_offset and literal address mode, so no need to use
// index and scale. Here set index as 0xffffffff and scale as 0x0.
operand indirect(iRegP reg)
%{
  constraint(ALLOC_IN_RC(ptr_reg));
  match(reg);
  op_cost(0);
  format %{ "[$reg]" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp(0x0);
  %}
%}

operand indOffI(iRegP reg, immIOffset off)
%{
  constraint(ALLOC_IN_RC(ptr_reg));
  match(AddP reg off);
  op_cost(0);
  format %{ "[$reg, $off]" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp($off);
  %}
%}

operand indOffL(iRegP reg, immLOffset off)
%{
  constraint(ALLOC_IN_RC(ptr_reg));
  match(AddP reg off);
  op_cost(0);
  format %{ "[$reg, $off]" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp($off);
  %}
%}

operand indirectN(iRegN reg)
%{
  predicate(CompressedOops::shift() == 0);
  constraint(ALLOC_IN_RC(ptr_reg));
  match(DecodeN reg);
  op_cost(0);
  format %{ "[$reg]\t# narrow" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp(0x0);
  %}
%}

operand indOffIN(iRegN reg, immIOffset off)
%{
  predicate(CompressedOops::shift() == 0);
  constraint(ALLOC_IN_RC(ptr_reg));
  match(AddP (DecodeN reg) off);
  op_cost(0);
  format %{ "[$reg, $off]\t# narrow" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp($off);
  %}
%}

operand indOffLN(iRegN reg, immLOffset off)
%{
  predicate(CompressedOops::shift() == 0);
  constraint(ALLOC_IN_RC(ptr_reg));
  match(AddP (DecodeN reg) off);
  op_cost(0);
  format %{ "[$reg, $off]\t# narrow" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp($off);
  %}
%}

// RISCV opto stubs need to write to the pc slot in the thread anchor
operand thread_anchor_pc(javaThread_RegP reg, immL_pc_off off)
%{
  constraint(ALLOC_IN_RC(ptr_reg));
  match(AddP reg off);
  op_cost(0);
  format %{ "[$reg, $off]" %}
  interface(MEMORY_INTER) %{
    base($reg);
    index(0xffffffff);
    scale(0x0);
    disp($off);
  %}
%}


//----------Special Memory Operands--------------------------------------------
// Stack Slot Operand - This operand is used for loading and storing temporary
//                      values on the stack where a match requires a value to
//                      flow through memory.
operand stackSlotI(sRegI reg)
%{
  constraint(ALLOC_IN_RC(stack_slots));
  // No match rule because this operand is only generated in matching
  // match(RegI);
  format %{ "[$reg]" %}
  interface(MEMORY_INTER) %{
    base(0x02);  // RSP
    index(0xffffffff);  // No Index
    scale(0x0);  // No Scale
    disp($reg);  // Stack Offset
  %}
%}

operand stackSlotF(sRegF reg)
%{
  constraint(ALLOC_IN_RC(stack_slots));
  // No match rule because this operand is only generated in matching
  // match(RegF);
  format %{ "[$reg]" %}
  interface(MEMORY_INTER) %{
    base(0x02);  // RSP
    index(0xffffffff);  // No Index
    scale(0x0);  // No Scale
    disp($reg);  // Stack Offset
  %}
%}

operand stackSlotD(sRegD reg)
%{
  constraint(ALLOC_IN_RC(stack_slots));
  // No match rule because this operand is only generated in matching
  // match(RegD);
  format %{ "[$reg]" %}
  interface(MEMORY_INTER) %{
    base(0x02);  // RSP
    index(0xffffffff);  // No Index
    scale(0x0);  // No Scale
    disp($reg);  // Stack Offset
  %}
%}

operand stackSlotL(sRegL reg)
%{
  constraint(ALLOC_IN_RC(stack_slots));
  // No match rule because this operand is only generated in matching
  // match(RegL);
  format %{ "[$reg]" %}
  interface(MEMORY_INTER) %{
    base(0x02);  // RSP
    index(0xffffffff);  // No Index
    scale(0x0);  // No Scale
    disp($reg);  // Stack Offset
  %}
%}

// Special operand allowing long args to int ops to be truncated for free

operand iRegL2I(iRegL reg) %{

  op_cost(0);

  match(ConvL2I reg);

  format %{ "l2i($reg)" %}

  interface(REG_INTER)
%}


// Comparison Operands
// NOTE: Label is a predefined operand which should not be redefined in
//       the AD file. It is generically handled within the ADLC.

//----------Conditional Branch Operands----------------------------------------
// Comparison Op  - This is the operation of the comparison, and is limited to
//                  the following set of codes:
//                  L (<), LE (<=), G (>), GE (>=), E (==), NE (!=)
//
// Other attributes of the comparison, such as unsignedness, are specified
// by the comparison instruction that sets a condition code flags register.
// That result is represented by a flags operand whose subtype is appropriate
// to the unsignedness (etc.) of the comparison.
//
// Later, the instruction which matches both the Comparison Op (a Bool) and
// the flags (produced by the Cmp) specifies the coding of the comparison op
// by matching a specific subtype of Bool operand below, such as cmpOpU.


// used for signed integral comparisons and fp comparisons
operand cmpOp()
%{
  match(Bool);

  format %{ "" %}

  // the values in interface derives from struct BoolTest::mask
  interface(COND_INTER) %{
    equal(0x0, "eq");
    greater(0x1, "gt");
    overflow(0x2, "overflow");
    less(0x3, "lt");
    not_equal(0x4, "ne");
    less_equal(0x5, "le");
    no_overflow(0x6, "no_overflow");
    greater_equal(0x7, "ge");
  %}
%}

// used for unsigned integral comparisons
operand cmpOpU()
%{
  match(Bool);

  format %{ "" %}
  // the values in interface derives from struct BoolTest::mask
  interface(COND_INTER) %{
    equal(0x0, "eq");
    greater(0x1, "gtu");
    overflow(0x2, "overflow");
    less(0x3, "ltu");
    not_equal(0x4, "ne");
    less_equal(0x5, "leu");
    no_overflow(0x6, "no_overflow");
    greater_equal(0x7, "geu");
  %}
%}

// used for certain integral comparisons which can be
// converted to bxx instructions
operand cmpOpEqNe()
%{
  match(Bool);
  op_cost(0);
  predicate(n->as_Bool()->_test._test == BoolTest::ne ||
            n->as_Bool()->_test._test == BoolTest::eq);

  format %{ "" %}
  interface(COND_INTER) %{
    equal(0x0, "eq");
    greater(0x1, "gt");
    overflow(0x2, "overflow");
    less(0x3, "lt");
    not_equal(0x4, "ne");
    less_equal(0x5, "le");
    no_overflow(0x6, "no_overflow");
    greater_equal(0x7, "ge");
  %}
%}

operand cmpOpULtGe()
%{
  match(Bool);
  op_cost(0);
  predicate(n->as_Bool()->_test._test == BoolTest::lt ||
            n->as_Bool()->_test._test == BoolTest::ge);

  format %{ "" %}
  interface(COND_INTER) %{
    equal(0x0, "eq");
    greater(0x1, "gt");
    overflow(0x2, "overflow");
    less(0x3, "lt");
    not_equal(0x4, "ne");
    less_equal(0x5, "le");
    no_overflow(0x6, "no_overflow");
    greater_equal(0x7, "ge");
  %}
%}

operand cmpOpUEqNeLeGt()
%{
  match(Bool);
  op_cost(0);
  predicate(n->as_Bool()->_test._test == BoolTest::ne ||
            n->as_Bool()->_test._test == BoolTest::eq ||
            n->as_Bool()->_test._test == BoolTest::le ||
            n->as_Bool()->_test._test == BoolTest::gt);

  format %{ "" %}
  interface(COND_INTER) %{
    equal(0x0, "eq");
    greater(0x1, "gt");
    overflow(0x2, "overflow");
    less(0x3, "lt");
    not_equal(0x4, "ne");
    less_equal(0x5, "le");
    no_overflow(0x6, "no_overflow");
    greater_equal(0x7, "ge");
  %}
%}


// Flags register, used as output of compare logic
operand rFlagsReg()
%{
  constraint(ALLOC_IN_RC(reg_flags));
  match(RegFlags);

  op_cost(0);
  format %{ "RFLAGS" %}
  interface(REG_INTER);
%}

// Special Registers

// Method Register
operand inline_cache_RegP(iRegP reg)
%{
  constraint(ALLOC_IN_RC(method_reg)); // inline_cache_reg
  match(reg);
  match(iRegPNoSp);
  op_cost(0);
  format %{ %}
  interface(REG_INTER);
%}

//----------OPERAND CLASSES----------------------------------------------------
// Operand Classes are groups of operands that are used as to simplify
// instruction definitions by not requiring the AD writer to specify
// separate instructions for every form of operand when the
// instruction accepts multiple operand types with the same basic
// encoding and format. The classic case of this is memory operands.

// memory is used to define read/write location for load/store
// instruction defs. we can turn a memory op into an Address

opclass memory(indirect, indOffI, indOffL, indirectN, indOffIN, indOffLN);

// iRegIorL2I is used for src inputs in rules for 32 bit int (I)
// operations. it allows the src to be either an iRegI or a (ConvL2I
// iRegL). in the latter case the l2i normally planted for a ConvL2I
// can be elided because the 32-bit instruction will just employ the
// lower 32 bits anyway.
//
// n.b. this does not elide all L2I conversions. if the truncated
// value is consumed by more than one operation then the ConvL2I
// cannot be bundled into the consuming nodes so an l2i gets planted
// (actually a mvw $dst $src) and the downstream instructions consume
// the result of the l2i as an iRegI input. That's a shame since the
// mvw is actually redundant but its not too costly.

opclass iRegIorL2I(iRegI, iRegL2I);
opclass iRegIorL(iRegI, iRegL);
opclass iRegNorP(iRegN, iRegP);
opclass iRegILNP(iRegI, iRegL, iRegN, iRegP);
opclass iRegILNPNoSp(iRegINoSp, iRegLNoSp, iRegNNoSp, iRegPNoSp);
opclass immIorL(immI, immL);

//----------PIPELINE-----------------------------------------------------------
// Rules which define the behavior of the target architectures pipeline.

// For specific pipelines, e.g. generic RISC-V, define the stages of that pipeline
//pipe_desc(ID, EX, MEM, WR);
#define ID   S0
#define EX   S1
#define MEM  S2
#define WR   S3

// Integer ALU reg operation
pipeline %{

attributes %{
  // RISC-V instructions are of fixed length
  fixed_size_instructions;           // Fixed size instructions TODO does
  max_instructions_per_bundle = 2;   // Generic RISC-V 1, Sifive Series 7 2
  // RISC-V instructions come in 32-bit word units
  instruction_unit_size = 4;         // An instruction is 4 bytes long
  instruction_fetch_unit_size = 64;  // The processor fetches one line
  instruction_fetch_units = 1;       // of 64 bytes

  // List of nop instructions
  nops( MachNop );
%}

// We don't use an actual pipeline model so don't care about resources
// or description. we do use pipeline classes to introduce fixed
// latencies

//----------RESOURCES----------------------------------------------------------
// Resources are the functional units available to the machine

// Generic RISC-V pipeline
// 1 decoder
// 1 instruction decoded per cycle
// 1 load/store ops per cycle, 1 branch, 1 FPU
// 1 mul, 1 div

resources ( DECODE,
            ALU,
            MUL,
            DIV,
            BRANCH,
            LDST,
            FPU);

//----------PIPELINE DESCRIPTION-----------------------------------------------
// Pipeline Description specifies the stages in the machine's pipeline

// Define the pipeline as a generic 6 stage pipeline
pipe_desc(S0, S1, S2, S3, S4, S5);

//----------PIPELINE CLASSES---------------------------------------------------
// Pipeline Classes describe the stages in which input and output are
// referenced by the hardware pipeline.

pipe_class fp_dop_reg_reg_s(fRegF dst, fRegF src1, fRegF src2)
%{
  single_instruction;
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_dop_reg_reg_d(fRegD dst, fRegD src1, fRegD src2)
%{
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_uop_s(fRegF dst, fRegF src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_uop_d(fRegD dst, fRegD src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_d2f(fRegF dst, fRegD src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_f2d(fRegD dst, fRegF src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_f2i(iRegINoSp dst, fRegF src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_f2l(iRegLNoSp dst, fRegF src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_i2f(fRegF dst, iRegIorL2I src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_l2f(fRegF dst, iRegL src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_d2i(iRegINoSp dst, fRegD src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_d2l(iRegLNoSp dst, fRegD src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_i2d(fRegD dst, iRegIorL2I src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_l2d(fRegD dst, iRegIorL2I src)
%{
  single_instruction;
  src    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_div_s(fRegF dst, fRegF src1, fRegF src2)
%{
  single_instruction;
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_div_d(fRegD dst, fRegD src1, fRegD src2)
%{
  single_instruction;
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_sqrt_s(fRegF dst, fRegF src1, fRegF src2)
%{
  single_instruction;
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_sqrt_d(fRegD dst, fRegD src1, fRegD src2)
%{
  single_instruction;
  src1   : S1(read);
  src2   : S2(read);
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_load_constant_s(fRegF dst)
%{
  single_instruction;
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_load_constant_d(fRegD dst)
%{
  single_instruction;
  dst    : S5(write);
  DECODE : ID;
  FPU    : S5;
%}

pipe_class fp_load_mem_s(fRegF dst, memory mem)
%{
  single_instruction;
  mem    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  LDST   : MEM;
%}

pipe_class fp_load_mem_d(fRegD dst, memory mem)
%{
  single_instruction;
  mem    : S1(read);
  dst    : S5(write);
  DECODE : ID;
  LDST   : MEM;
%}

pipe_class fp_store_reg_s(fRegF src, memory mem)
%{
  single_instruction;
  src    : S1(read);
  mem    : S5(write);
  DECODE : ID;
  LDST   : MEM;
%}

pipe_class fp_store_reg_d(fRegD src, memory mem)
%{
  single_instruction;
  src    : S1(read);
  mem    : S5(write);
  DECODE : ID;
  LDST   : MEM;
%}

//------- Integer ALU operations --------------------------

// Integer ALU reg-reg operation
// Operands needs in ID, result generated in EX
// E.g.  ADD   Rd, Rs1, Rs2
pipe_class ialu_reg_reg(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  dst    : EX(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  ALU    : EX;
%}

// Integer ALU reg operation with constant shift
// E.g. SLLI    Rd, Rs1, #shift
pipe_class ialu_reg_shift(iRegI dst, iRegI src1)
%{
  single_instruction;
  dst    : EX(write);
  src1   : ID(read);
  DECODE : ID;
  ALU    : EX;
%}

// Integer ALU reg-reg operation with variable shift
// both operands must be available in ID
// E.g. SLL   Rd, Rs1, Rs2
pipe_class ialu_reg_reg_vshift(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  dst    : EX(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  ALU    : EX;
%}

// Integer ALU reg operation
// E.g. NEG   Rd, Rs2
pipe_class ialu_reg(iRegI dst, iRegI src)
%{
  single_instruction;
  dst    : EX(write);
  src    : ID(read);
  DECODE : ID;
  ALU    : EX;
%}

// Integer ALU reg immediate operation
// E.g. ADDI   Rd, Rs1, #imm
pipe_class ialu_reg_imm(iRegI dst, iRegI src1)
%{
  single_instruction;
  dst    : EX(write);
  src1   : ID(read);
  DECODE : ID;
  ALU    : EX;
%}

// Integer ALU immediate operation (no source operands)
// E.g. LI    Rd, #imm
pipe_class ialu_imm(iRegI dst)
%{
  single_instruction;
  dst    : EX(write);
  DECODE : ID;
  ALU    : EX;
%}

//------- Multiply pipeline operations --------------------

// Multiply reg-reg
// E.g. MULW   Rd, Rs1, Rs2
pipe_class imul_reg_reg(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  dst    : WR(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  MUL    : WR;
%}

// E.g. MUL   RD, Rs1, Rs2
pipe_class lmul_reg_reg(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  fixed_latency(3); // Maximum latency for 64 bit mul
  dst    : WR(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  MUL    : WR;
%}

//------- Divide pipeline operations --------------------

// E.g. DIVW   Rd, Rs1, Rs2
pipe_class idiv_reg_reg(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  fixed_latency(8); // Maximum latency for 32 bit divide
  dst    : WR(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  DIV    : WR;
%}

// E.g. DIV   RD, Rs1, Rs2
pipe_class ldiv_reg_reg(iRegI dst, iRegI src1, iRegI src2)
%{
  single_instruction;
  fixed_latency(16); // Maximum latency for 64 bit divide
  dst    : WR(write);
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  DIV    : WR;
%}

//------- Load pipeline operations ------------------------

// Load - prefetch
// Eg.  PREFETCH_W  mem
pipe_class iload_prefetch(memory mem)
%{
  single_instruction;
  mem    : ID(read);
  DECODE : ID;
  LDST   : MEM;
%}

// Load - reg, mem
// E.g. LA    Rd, mem
pipe_class iload_reg_mem(iRegI dst, memory mem)
%{
  single_instruction;
  dst    : WR(write);
  mem    : ID(read);
  DECODE : ID;
  LDST   : MEM;
%}

// Load - reg, reg
// E.g. LD    Rd, Rs
pipe_class iload_reg_reg(iRegI dst, iRegI src)
%{
  single_instruction;
  dst    : WR(write);
  src    : ID(read);
  DECODE : ID;
  LDST   : MEM;
%}

//------- Store pipeline operations -----------------------

// Store - zr, mem
// E.g. SD    zr, mem
pipe_class istore_mem(memory mem)
%{
  single_instruction;
  mem    : ID(read);
  DECODE : ID;
  LDST   : MEM;
%}

// Store - reg, mem
// E.g. SD    Rs, mem
pipe_class istore_reg_mem(iRegI src, memory mem)
%{
  single_instruction;
  mem    : ID(read);
  src    : EX(read);
  DECODE : ID;
  LDST   : MEM;
%}

// Store - reg, reg
// E.g. SD    Rs2, Rs1
pipe_class istore_reg_reg(iRegI dst, iRegI src)
%{
  single_instruction;
  dst    : ID(read);
  src    : EX(read);
  DECODE : ID;
  LDST   : MEM;
%}

//------- Store pipeline operations -----------------------

// Branch
pipe_class pipe_branch()
%{
  single_instruction;
  DECODE : ID;
  BRANCH : EX;
%}

// Branch
pipe_class pipe_branch_reg(iRegI src)
%{
  single_instruction;
  src    : ID(read);
  DECODE : ID;
  BRANCH : EX;
%}

// Compare & Branch
// E.g. BEQ   Rs1, Rs2, L
pipe_class pipe_cmp_branch(iRegI src1, iRegI src2)
%{
  single_instruction;
  src1   : ID(read);
  src2   : ID(read);
  DECODE : ID;
  BRANCH : EX;
%}

// E.g. BEQZ Rs, L
pipe_class pipe_cmpz_branch(iRegI src)
%{
  single_instruction;
  src    : ID(read);
  DECODE : ID;
  BRANCH : EX;
%}

//------- Synchronisation operations ----------------------
// Any operation requiring serialization
// E.g. FENCE/Atomic Ops/Load Acquire/Store Release
pipe_class pipe_serial()
%{
  single_instruction;
  force_serialization;
  fixed_latency(16);
  DECODE : ID;
  LDST   : MEM;
%}

pipe_class pipe_slow()
%{
  instruction_count(10);
  multiple_bundles;
  force_serialization;
  fixed_latency(16);
  DECODE : ID;
  LDST   : MEM;
%}

// Empty pipeline class
pipe_class pipe_class_empty()
%{
  single_instruction;
  fixed_latency(0);
%}

// Default pipeline class.
pipe_class pipe_class_default()
%{
  single_instruction;
  fixed_latency(2);
%}

// Pipeline class for compares.
pipe_class pipe_class_compare()
%{
  single_instruction;
  fixed_latency(16);
%}

// Pipeline class for memory operations.
pipe_class pipe_class_memory()
%{
  single_instruction;
  fixed_latency(16);
%}

// Pipeline class for call.
pipe_class pipe_class_call()
%{
  single_instruction;
  fixed_latency(100);
%}

// Define the class for the Nop node.
define %{
   MachNop = pipe_class_empty;
%}
%}
//----------INSTRUCTIONS-------------------------------------------------------
//
// match      -- States which machine-independent subtree may be replaced
//               by this instruction.
// ins_cost   -- The estimated cost of this instruction is used by instruction
//               selection to identify a minimum cost tree of machine
//               instructions that matches a tree of machine-independent
//               instructions.
// format     -- A string providing the disassembly for this instruction.
//               The value of an instruction's operand may be inserted
//               by referring to it with a '$' prefix.
// opcode     -- Three instruction opcodes may be provided.  These are referred
//               to within an encode class as $primary, $secondary, and $tertiary
//               rrspectively.  The primary opcode is commonly used to
//               indicate the type of machine instruction, while secondary
//               and tertiary are often used for prefix options or addressing
//               modes.
// ins_encode -- A list of encode classes with parameters. The encode class
//               name must have been defined in an 'enc_class' specification
//               in the encode section of the architecture description.

// ============================================================================
// Memory (Load/Store) Instructions

// Load Instructions

// Load Byte (8 bit signed)
instruct loadB(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadB mem));

  ins_cost(LOAD_COST);
  format %{ "lb  $dst, $mem\t# byte, #@loadB" %}

  ins_encode %{
    __ lb(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Byte (8 bit signed) into long
instruct loadB2L(iRegLNoSp dst, memory mem)
%{
  match(Set dst (ConvI2L (LoadB mem)));

  ins_cost(LOAD_COST);
  format %{ "lb  $dst, $mem\t# byte, #@loadB2L" %}

  ins_encode %{
    __ lb(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Byte (8 bit unsigned)
instruct loadUB(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadUB mem));

  ins_cost(LOAD_COST);
  format %{ "lbu  $dst, $mem\t# byte, #@loadUB" %}

  ins_encode %{
    __ lbu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Byte (8 bit unsigned) into long
instruct loadUB2L(iRegLNoSp dst, memory mem)
%{
  match(Set dst (ConvI2L (LoadUB mem)));

  ins_cost(LOAD_COST);
  format %{ "lbu  $dst, $mem\t# byte, #@loadUB2L" %}

  ins_encode %{
    __ lbu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Short (16 bit signed)
instruct loadS(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadS mem));

  ins_cost(LOAD_COST);
  format %{ "lh  $dst, $mem\t# short, #@loadS" %}

  ins_encode %{
    __ lh(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Short (16 bit signed) into long
instruct loadS2L(iRegLNoSp dst, memory mem)
%{
  match(Set dst (ConvI2L (LoadS mem)));

  ins_cost(LOAD_COST);
  format %{ "lh  $dst, $mem\t# short, #@loadS2L" %}

  ins_encode %{
    __ lh(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Char (16 bit unsigned)
instruct loadUS(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadUS mem));

  ins_cost(LOAD_COST);
  format %{ "lhu  $dst, $mem\t# short, #@loadUS" %}

  ins_encode %{
    __ lhu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Short/Char (16 bit unsigned) into long
instruct loadUS2L(iRegLNoSp dst, memory mem)
%{
  match(Set dst (ConvI2L (LoadUS mem)));

  ins_cost(LOAD_COST);
  format %{ "lhu  $dst, $mem\t# short, #@loadUS2L" %}

  ins_encode %{
    __ lhu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Integer (32 bit signed)
instruct loadI(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadI mem));

  ins_cost(LOAD_COST);
  format %{ "lw  $dst, $mem\t# int, #@loadI" %}

  ins_encode %{
    __ lw(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Integer (32 bit signed) into long
instruct loadI2L(iRegLNoSp dst, memory mem)
%{
  match(Set dst (ConvI2L (LoadI mem)));

  ins_cost(LOAD_COST);
  format %{ "lw  $dst, $mem\t# int, #@loadI2L" %}

  ins_encode %{
    __ lw(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Integer (32 bit unsigned) into long
instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask)
%{
  match(Set dst (AndL (ConvI2L (LoadI mem)) mask));

  ins_cost(LOAD_COST);
  format %{ "lwu  $dst, $mem\t# int, #@loadUI2L" %}

  ins_encode %{
    __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Long (64 bit signed)
instruct loadL(iRegLNoSp dst, memory mem)
%{
  match(Set dst (LoadL mem));

  ins_cost(LOAD_COST);
  format %{ "ld  $dst, $mem\t# int, #@loadL" %}

  ins_encode %{
    __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Range
instruct loadRange(iRegINoSp dst, memory mem)
%{
  match(Set dst (LoadRange mem));

  ins_cost(LOAD_COST);
  format %{ "lwu  $dst, $mem\t# range, #@loadRange" %}

  ins_encode %{
    __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Pointer
instruct loadP(iRegPNoSp dst, memory mem)
%{
  match(Set dst (LoadP mem));
  predicate(n->as_Load()->barrier_data() == 0);

  ins_cost(LOAD_COST);
  format %{ "ld  $dst, $mem\t# ptr, #@loadP" %}

  ins_encode %{
    __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Compressed Pointer
instruct loadN(iRegNNoSp dst, memory mem)
%{
  match(Set dst (LoadN mem));

  ins_cost(LOAD_COST);
  format %{ "lwu  $dst, $mem\t# loadN, compressed ptr, #@loadN" %}

  ins_encode %{
    __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Klass Pointer
instruct loadKlass(iRegPNoSp dst, memory mem)
%{
  match(Set dst (LoadKlass mem));

  ins_cost(LOAD_COST);
  format %{ "ld  $dst, $mem\t# class, #@loadKlass" %}

  ins_encode %{
    __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Narrow Klass Pointer
instruct loadNKlass(iRegNNoSp dst, memory mem)
%{
  match(Set dst (LoadNKlass mem));

  ins_cost(LOAD_COST);
  format %{ "lwu  $dst, $mem\t# loadNKlass, compressed class ptr, #@loadNKlass" %}

  ins_encode %{
    __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(iload_reg_mem);
%}

// Load Float
instruct loadF(fRegF dst, memory mem)
%{
  match(Set dst (LoadF mem));

  ins_cost(LOAD_COST);
  format %{ "flw  $dst, $mem\t# float, #@loadF" %}

  ins_encode %{
    __ flw(as_FloatRegister($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(fp_load_mem_s);
%}

// Load Double
instruct loadD(fRegD dst, memory mem)
%{
  match(Set dst (LoadD mem));

  ins_cost(LOAD_COST);
  format %{ "fld  $dst, $mem\t# double, #@loadD" %}

  ins_encode %{
    __ fld(as_FloatRegister($dst$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(fp_load_mem_d);
%}

// Load Int Constant
instruct loadConI(iRegINoSp dst, immI src)
%{
  match(Set dst src);

  ins_cost(ALU_COST);
  format %{ "li $dst, $src\t# int, #@loadConI" %}

  ins_encode(riscv_enc_li_imm(dst, src));

  ins_pipe(ialu_imm);
%}

// Load Long Constant
instruct loadConL(iRegLNoSp dst, immL src)
%{
  match(Set dst src);

  ins_cost(ALU_COST);
  format %{ "li $dst, $src\t# long, #@loadConL" %}

  ins_encode(riscv_enc_li_imm(dst, src));

  ins_pipe(ialu_imm);
%}

// Load Pointer Constant
instruct loadConP(iRegPNoSp dst, immP con)
%{
  match(Set dst con);

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $con\t# ptr, #@loadConP" %}

  ins_encode(riscv_enc_mov_p(dst, con));

  ins_pipe(ialu_imm);
%}

// Load Null Pointer Constant
instruct loadConP0(iRegPNoSp dst, immP0 con)
%{
  match(Set dst con);

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $con\t# NULL ptr, #@loadConP0" %}

  ins_encode(riscv_enc_mov_zero(dst));

  ins_pipe(ialu_imm);
%}

// Load Pointer Constant One
instruct loadConP1(iRegPNoSp dst, immP_1 con)
%{
  match(Set dst con);

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $con\t# load ptr constant one, #@loadConP1" %}

  ins_encode(riscv_enc_mov_p1(dst));

  ins_pipe(ialu_imm);
%}

// Load Byte Map Base Constant
instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con)
%{
  match(Set dst con);
  ins_cost(ALU_COST);
  format %{ "mv  $dst, $con\t# Byte Map Base, #@loadByteMapBase" %}

  ins_encode(riscv_enc_mov_byte_map_base(dst));

  ins_pipe(ialu_imm);
%}

// Load Narrow Pointer Constant
instruct loadConN(iRegNNoSp dst, immN con)
%{
  match(Set dst con);

  ins_cost(ALU_COST * 4);
  format %{ "mv  $dst, $con\t# compressed ptr, #@loadConN" %}

  ins_encode(riscv_enc_mov_n(dst, con));

  ins_pipe(ialu_imm);
%}

// Load Narrow Null Pointer Constant
instruct loadConN0(iRegNNoSp dst, immN0 con)
%{
  match(Set dst con);

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $con\t# compressed NULL ptr, #@loadConN0" %}

  ins_encode(riscv_enc_mov_zero(dst));

  ins_pipe(ialu_imm);
%}

// Load Narrow Klass Constant
instruct loadConNKlass(iRegNNoSp dst, immNKlass con)
%{
  match(Set dst con);

  ins_cost(ALU_COST * 6);
  format %{ "mv  $dst, $con\t# compressed klass ptr, #@loadConNKlass" %}

  ins_encode(riscv_enc_mov_nk(dst, con));

  ins_pipe(ialu_imm);
%}

// Load Float Constant
instruct loadConF(fRegF dst, immF con) %{
  match(Set dst con);

  ins_cost(LOAD_COST);
  format %{
    "flw $dst, [$constantaddress]\t# load from constant table: float=$con, #@loadConF"
  %}

  ins_encode %{
    __ flw(as_FloatRegister($dst$$reg), $constantaddress($con));
  %}

  ins_pipe(fp_load_constant_s);
%}

instruct loadConF0(fRegF dst, immF0 con) %{
  match(Set dst con);

  ins_cost(XFER_COST);

  format %{ "fmv.w.x $dst, zr\t# float, #@loadConF0" %}

  ins_encode %{
    __ fmv_w_x(as_FloatRegister($dst$$reg), zr);
  %}

  ins_pipe(fp_load_constant_s);
%}

// Load Double Constant
instruct loadConD(fRegD dst, immD con) %{
  match(Set dst con);

  ins_cost(LOAD_COST);
  format %{
    "fld $dst, [$constantaddress]\t# load from constant table: double=$con, #@loadConD"
  %}

  ins_encode %{
    __ fld(as_FloatRegister($dst$$reg), $constantaddress($con));
  %}

  ins_pipe(fp_load_constant_d);
%}

instruct loadConD0(fRegD dst, immD0 con) %{
  match(Set dst con);

  ins_cost(XFER_COST);

  format %{ "fmv.d.x $dst, zr\t# double, #@loadConD0" %}

  ins_encode %{
    __ fmv_d_x(as_FloatRegister($dst$$reg), zr);
  %}

  ins_pipe(fp_load_constant_d);
%}

// Store Instructions
// Store CMS card-mark Immediate
instruct storeimmCM0(immI0 zero, memory mem)
%{
  match(Set mem (StoreCM mem zero));

  ins_cost(STORE_COST);
  format %{ "storestore (elided)\n\t"
            "sb zr, $mem\t# byte, #@storeimmCM0" %}

  ins_encode %{
    __ sb(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store CMS card-mark Immediate with intervening StoreStore
// needed when using CMS with no conditional card marking
instruct storeimmCM0_ordered(immI0 zero, memory mem)
%{
  match(Set mem (StoreCM mem zero));

  ins_cost(ALU_COST + STORE_COST);
  format %{ "membar(StoreStore)\n\t"
            "sb zr, $mem\t# byte, #@storeimmCM0_ordered" %}

  ins_encode %{
    __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
    __ sb(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Byte
instruct storeB(iRegIorL2I src, memory mem)
%{
  match(Set mem (StoreB mem src));

  ins_cost(STORE_COST);
  format %{ "sb  $src, $mem\t# byte, #@storeB" %}

  ins_encode %{
    __ sb(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

instruct storeimmB0(immI0 zero, memory mem)
%{
  match(Set mem (StoreB mem zero));

  ins_cost(STORE_COST);
  format %{ "sb zr, $mem\t# byte, #@storeimmB0" %}

  ins_encode %{
    __ sb(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Char/Short
instruct storeC(iRegIorL2I src, memory mem)
%{
  match(Set mem (StoreC mem src));

  ins_cost(STORE_COST);
  format %{ "sh  $src, $mem\t# short, #@storeC" %}

  ins_encode %{
    __ sh(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

instruct storeimmC0(immI0 zero, memory mem)
%{
  match(Set mem (StoreC mem zero));

  ins_cost(STORE_COST);
  format %{ "sh  zr, $mem\t# short, #@storeimmC0" %}

  ins_encode %{
    __ sh(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Integer
instruct storeI(iRegIorL2I src, memory mem)
%{
  match(Set mem(StoreI mem src));

  ins_cost(STORE_COST);
  format %{ "sw  $src, $mem\t# int, #@storeI" %}

  ins_encode %{
    __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

instruct storeimmI0(immI0 zero, memory mem)
%{
  match(Set mem(StoreI mem zero));

  ins_cost(STORE_COST);
  format %{ "sw  zr, $mem\t# int, #@storeimmI0" %}

  ins_encode %{
    __ sw(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Long (64 bit signed)
instruct storeL(iRegL src, memory mem)
%{
  match(Set mem (StoreL mem src));

  ins_cost(STORE_COST);
  format %{ "sd  $src, $mem\t# long, #@storeL" %}

  ins_encode %{
    __ sd(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

// Store Long (64 bit signed)
instruct storeimmL0(immL0 zero, memory mem)
%{
  match(Set mem (StoreL mem zero));

  ins_cost(STORE_COST);
  format %{ "sd  zr, $mem\t# long, #@storeimmL0" %}

  ins_encode %{
    __ sd(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Pointer
instruct storeP(iRegP src, memory mem)
%{
  match(Set mem (StoreP mem src));

  ins_cost(STORE_COST);
  format %{ "sd  $src, $mem\t# ptr, #@storeP" %}

  ins_encode %{
    __ sd(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

// Store Pointer
instruct storeimmP0(immP0 zero, memory mem)
%{
  match(Set mem (StoreP mem zero));

  ins_cost(STORE_COST);
  format %{ "sd zr, $mem\t# ptr, #@storeimmP0" %}

  ins_encode %{
    __ sd(zr, Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_mem);
%}

// Store Compressed Pointer
instruct storeN(iRegN src, memory mem)
%{
  match(Set mem (StoreN mem src));

  ins_cost(STORE_COST);
  format %{ "sw  $src, $mem\t# compressed ptr, #@storeN" %}

  ins_encode %{
    __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

instruct storeImmN0(iRegIHeapbase heapbase, immN0 zero, memory mem)
%{
  match(Set mem (StoreN mem zero));

  ins_cost(STORE_COST);
  format %{ "sw  rheapbase, $mem\t# compressed ptr (rheapbase==0), #@storeImmN0" %}

  ins_encode %{
    __ sw(as_Register($heapbase$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

// Store Float
instruct storeF(fRegF src, memory mem)
%{
  match(Set mem (StoreF mem src));

  ins_cost(STORE_COST);
  format %{ "fsw  $src, $mem\t# float, #@storeF" %}

  ins_encode %{
    __ fsw(as_FloatRegister($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(fp_store_reg_s);
%}

// Store Double
instruct storeD(fRegD src, memory mem)
%{
  match(Set mem (StoreD mem src));

  ins_cost(STORE_COST);
  format %{ "fsd  $src, $mem\t# double, #@storeD" %}

  ins_encode %{
    __ fsd(as_FloatRegister($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(fp_store_reg_d);
%}

// Store Compressed Klass Pointer
instruct storeNKlass(iRegN src, memory mem)
%{
  match(Set mem (StoreNKlass mem src));

  ins_cost(STORE_COST);
  format %{ "sw  $src, $mem\t# compressed klass ptr, #@storeNKlass" %}

  ins_encode %{
    __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp));
  %}

  ins_pipe(istore_reg_mem);
%}

// ============================================================================
// Prefetch instructions
// Must be safe to execute with invalid address (cannot fault).

instruct prefetchalloc( memory mem ) %{
  predicate(UseZicbop);
  match(PrefetchAllocation mem);

  ins_cost(ALU_COST * 1);
  format %{ "prefetch_w $mem\t# Prefetch for write" %}

  ins_encode %{
    if (is_imm_in_range($mem$$disp, 12, 0)) {
      if (($mem$$disp & 0x1f) == 0) {
        __ prefetch_w(as_Register($mem$$base), $mem$$disp);
      } else {
        __ addi(t0, as_Register($mem$$base), $mem$$disp);
        __ prefetch_w(t0, 0);
      }
    } else {
      __ mv(t0, $mem$$disp);
      __ add(t0, as_Register($mem$$base), t0);
      __ prefetch_w(t0, 0);
    }
  %}

  ins_pipe(iload_prefetch);
%}

// ============================================================================
// Atomic operation instructions
//

// standard CompareAndSwapX when we are using barriers
// these have higher priority than the rules selected by a predicate
instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                         iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (CompareAndSwapB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 10 + BRANCH_COST * 4);

  effect(TEMP_DEF res, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (byte) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapB"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                            Assembler::relaxed /* acquire */, Assembler::rl /* release */, $res$$Register,
                            true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                         iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (CompareAndSwapS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 11 + BRANCH_COST * 4);

  effect(TEMP_DEF res, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (short) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapS"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                            Assembler::relaxed /* acquire */, Assembler::rl /* release */, $res$$Register,
                            true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  match(Set res (CompareAndSwapI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (int) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapI"
  %}

  ins_encode(riscv_enc_cmpxchgw(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  match(Set res (CompareAndSwapL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (long) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapL"
  %}

  ins_encode(riscv_enc_cmpxchg(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(n->as_LoadStore()->barrier_data() == 0);

  match(Set res (CompareAndSwapP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapP"
  %}

  ins_encode(riscv_enc_cmpxchg(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  match(Set res (CompareAndSwapN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 8 + BRANCH_COST * 4);

  format %{
    "cmpxchg $mem, $oldval, $newval\t# (narrow oop) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapN"
  %}

  ins_encode(riscv_enc_cmpxchgn(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

// alternative CompareAndSwapX when we are eliding barriers
instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                            iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndSwapB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 10 + BRANCH_COST * 4);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (byte) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapBAcq"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                            Assembler::aq /* acquire */, Assembler::rl /* release */, $res$$Register,
                            true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                            iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndSwapS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 11 + BRANCH_COST * 4);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (short) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapSAcq"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                            Assembler::aq /* acquire */, Assembler::rl /* release */, $res$$Register,
                            true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndSwapI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (int) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapIAcq"
  %}

  ins_encode(riscv_enc_cmpxchgw_acq(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndSwapL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (long) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapLAcq"
  %}

  ins_encode(riscv_enc_cmpxchg_acq(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0));

  match(Set res (CompareAndSwapP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapPAcq"
  %}

  ins_encode(riscv_enc_cmpxchg_acq(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndSwapN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + ALU_COST * 8 + BRANCH_COST * 4);

  format %{
    "cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop) if $mem == $oldval then $mem <-- $newval\n\t"
    "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapNAcq"
  %}

  ins_encode(riscv_enc_cmpxchgn_acq(res, mem, oldval, newval));

  ins_pipe(pipe_slow);
%}

// Sundry CAS operations.  Note that release is always true,
// regardless of the memory ordering of the CAS.  This is because we
// need the volatile case to be sequentially consistent but there is
// no trailing StoreLoad barrier emitted by C2.  Unfortunately we
// can't check the type of memory ordering here, so we always emit a
// sc_d(w) with rl bit set.
instruct compareAndExchangeB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                             iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (CompareAndExchangeB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 5);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg $res = $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeB"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                            /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
                            /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                             iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (CompareAndExchangeS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 6);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg $res = $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeS"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                            /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
                            /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  match(Set res (CompareAndExchangeI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg $res = $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeI"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  match(Set res (CompareAndExchangeL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg $res = $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeL"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  match(Set res (CompareAndExchangeN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 3);

  effect(TEMP_DEF res);

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

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(n->as_LoadStore()->barrier_data() == 0);
  match(Set res (CompareAndExchangeP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeP"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                                iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndExchangeB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 5);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_acq $res = $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeBAcq"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                            /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
                            /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                                iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndExchangeS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 6);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_acq $res = $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeSAcq"
  %}

  ins_encode %{
    __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                            /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
                            /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndExchangeI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg_acq $res = $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeIAcq"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndExchangeL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg_acq $res = $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeLAcq"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (CompareAndExchangeN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

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

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0));

  match(Set res (CompareAndExchangeP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST);

  effect(TEMP_DEF res);

  format %{
    "cmpxchg_acq $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangePAcq"
  %}

  ins_encode %{
    __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
               /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                             iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (WeakCompareAndSwapB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 6);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapB"
  %}

  ins_encode %{
    __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                                 /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
                                 $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                             iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  match(Set res (WeakCompareAndSwapS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 7);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapS"
  %}

  ins_encode %{
    __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                                 /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register,
                                 $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  match(Set res (WeakCompareAndSwapI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapI"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
                    /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  match(Set res (WeakCompareAndSwapL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapL"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
                    /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  match(Set res (WeakCompareAndSwapN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapN"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
                    /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(n->as_LoadStore()->barrier_data() == 0);
  match(Set res (WeakCompareAndSwapP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapP"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
                    /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                                iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (WeakCompareAndSwapB mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 6);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapBAcq"
  %}

  ins_encode %{
    __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8,
                                 /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
                                 $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval,
                                iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (WeakCompareAndSwapS mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 7);

  effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapSAcq"
  %}

  ins_encode %{
    __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16,
                                 /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register,
                                 $tmp1$$Register, $tmp2$$Register, $tmp3$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (WeakCompareAndSwapI mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapIAcq"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32,
                    /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (WeakCompareAndSwapL mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapLAcq"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
                    /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set res (WeakCompareAndSwapN mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "# $res == 1 when success, #@weakCompareAndSwapNAcq"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32,
                    /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval)
%{
  predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0));

  match(Set res (WeakCompareAndSwapP mem (Binary oldval newval)));

  ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2);

  format %{
    "cmpxchg_weak_acq $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t"
    "\t# $res == 1 when success, #@weakCompareAndSwapPAcq"
  %}

  ins_encode %{
    __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64,
                    /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register);
  %}

  ins_pipe(pipe_slow);
%}

instruct get_and_setI(indirect mem, iRegI newv, iRegINoSp prev)
%{
  match(Set prev (GetAndSetI mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchgw  $prev, $newv, [$mem]\t#@get_and_setI" %}

  ins_encode %{
    __ atomic_xchgw($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setL(indirect mem, iRegL newv, iRegLNoSp prev)
%{
  match(Set prev (GetAndSetL mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchg  $prev, $newv, [$mem]\t#@get_and_setL" %}

  ins_encode %{
    __ atomic_xchg($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setN(indirect mem, iRegN newv, iRegINoSp prev)
%{
  match(Set prev (GetAndSetN mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchgwu $prev, $newv, [$mem]\t#@get_and_setN" %}

  ins_encode %{
    __ atomic_xchgwu($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setP(indirect mem, iRegP newv, iRegPNoSp prev)
%{
  predicate(n->as_LoadStore()->barrier_data() == 0);
  match(Set prev (GetAndSetP mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchg  $prev, $newv, [$mem]\t#@get_and_setP" %}

  ins_encode %{
    __ atomic_xchg($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setIAcq(indirect mem, iRegI newv, iRegINoSp prev)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set prev (GetAndSetI mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchgw_acq  $prev, $newv, [$mem]\t#@get_and_setIAcq" %}

  ins_encode %{
    __ atomic_xchgalw($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setLAcq(indirect mem, iRegL newv, iRegLNoSp prev)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set prev (GetAndSetL mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchg_acq  $prev, $newv, [$mem]\t#@get_and_setLAcq" %}

  ins_encode %{
    __ atomic_xchgal($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setNAcq(indirect mem, iRegN newv, iRegINoSp prev)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set prev (GetAndSetN mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchgwu_acq $prev, $newv, [$mem]\t#@get_and_setNAcq" %}

  ins_encode %{
    __ atomic_xchgalwu($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_setPAcq(indirect mem, iRegP newv, iRegPNoSp prev)
%{
  predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0));

  match(Set prev (GetAndSetP mem newv));

  ins_cost(ALU_COST);

  format %{ "atomic_xchg_acq  $prev, $newv, [$mem]\t#@get_and_setPAcq" %}

  ins_encode %{
    __ atomic_xchgal($prev$$Register, $newv$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addL(indirect mem, iRegLNoSp newval, iRegL incr)
%{
  match(Set newval (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL $newval, [$mem], $incr\t#@get_and_addL" %}

  ins_encode %{
    __ atomic_add($newval$$Register, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addL_no_res(indirect mem, Universe dummy, iRegL incr)
%{
  predicate(n->as_LoadStore()->result_not_used());

  match(Set dummy (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL [$mem], $incr\t#@get_and_addL_no_res" %}

  ins_encode %{
    __ atomic_add(noreg, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addLi(indirect mem, iRegLNoSp newval, immLAdd incr)
%{
  match(Set newval (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL $newval, [$mem], $incr\t#@get_and_addLi" %}

  ins_encode %{
    __ atomic_add($newval$$Register, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addLi_no_res(indirect mem, Universe dummy, immLAdd incr)
%{
  predicate(n->as_LoadStore()->result_not_used());

  match(Set dummy (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL [$mem], $incr\t#@get_and_addLi_no_res" %}

  ins_encode %{
    __ atomic_add(noreg, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addI(indirect mem, iRegINoSp newval, iRegIorL2I incr)
%{
  match(Set newval (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI $newval, [$mem], $incr\t#@get_and_addI" %}

  ins_encode %{
    __ atomic_addw($newval$$Register, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addI_no_res(indirect mem, Universe dummy, iRegIorL2I incr)
%{
  predicate(n->as_LoadStore()->result_not_used());

  match(Set dummy (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI [$mem], $incr\t#@get_and_addI_no_res" %}

  ins_encode %{
    __ atomic_addw(noreg, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addIi(indirect mem, iRegINoSp newval, immIAdd incr)
%{
  match(Set newval (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI $newval, [$mem], $incr\t#@get_and_addIi" %}

  ins_encode %{
    __ atomic_addw($newval$$Register, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addIi_no_res(indirect mem, Universe dummy, immIAdd incr)
%{
  predicate(n->as_LoadStore()->result_not_used());

  match(Set dummy (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI [$mem], $incr\t#@get_and_addIi_no_res" %}

  ins_encode %{
    __ atomic_addw(noreg, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addLAcq(indirect mem, iRegLNoSp newval, iRegL incr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set newval (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL_acq $newval, [$mem], $incr\t#@get_and_addLAcq" %}

  ins_encode %{
    __ atomic_addal($newval$$Register, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addL_no_resAcq(indirect mem, Universe dummy, iRegL incr) %{
  predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n));

  match(Set dummy (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL_acq [$mem], $incr\t#@get_and_addL_no_resAcq" %}

  ins_encode %{
    __ atomic_addal(noreg, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addLiAcq(indirect mem, iRegLNoSp newval, immLAdd incr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set newval (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL_acq $newval, [$mem], $incr\t#@get_and_addLiAcq" %}

  ins_encode %{
    __ atomic_addal($newval$$Register, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addLi_no_resAcq(indirect mem, Universe dummy, immLAdd incr)
%{
  predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n));

  match(Set dummy (GetAndAddL mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addL_acq [$mem], $incr\t#@get_and_addLi_no_resAcq" %}

  ins_encode %{
    __ atomic_addal(noreg, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addIAcq(indirect mem, iRegINoSp newval, iRegIorL2I incr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set newval (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI_acq $newval, [$mem], $incr\t#@get_and_addIAcq" %}

  ins_encode %{
    __ atomic_addalw($newval$$Register, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addI_no_resAcq(indirect mem, Universe dummy, iRegIorL2I incr)
%{
  predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n));

  match(Set dummy (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI_acq [$mem], $incr\t#@get_and_addI_no_resAcq" %}

  ins_encode %{
    __ atomic_addalw(noreg, $incr$$Register, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addIiAcq(indirect mem, iRegINoSp newval, immIAdd incr)
%{
  predicate(needs_acquiring_load_reserved(n));

  match(Set newval (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI_acq $newval, [$mem], $incr\t#@get_and_addIiAcq" %}

  ins_encode %{
    __ atomic_addalw($newval$$Register, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

instruct get_and_addIi_no_resAcq(indirect mem, Universe dummy, immIAdd incr)
%{
  predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n));

  match(Set dummy (GetAndAddI mem incr));

  ins_cost(ALU_COST);

  format %{ "get_and_addI_acq [$mem], $incr\t#@get_and_addIi_no_resAcq" %}

  ins_encode %{
    __ atomic_addalw(noreg, $incr$$constant, as_Register($mem$$base));
  %}

  ins_pipe(pipe_serial);
%}

// ============================================================================
// Arithmetic Instructions
//

// Integer Addition

// TODO
// these currently employ operations which do not set CR and hence are
// not flagged as killing CR but we would like to isolate the cases
// where we want to set flags from those where we don't. need to work
// out how to do that.
instruct addI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (AddI src1 src2));

  ins_cost(ALU_COST);
  format %{ "addw  $dst, $src1, $src2\t#@addI_reg_reg" %}

  ins_encode %{
    __ addw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

instruct addI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immIAdd src2) %{
  match(Set dst (AddI src1 src2));

  ins_cost(ALU_COST);
  format %{ "addiw  $dst, $src1, $src2\t#@addI_reg_imm" %}

  ins_encode %{
    int32_t con = (int32_t)$src2$$constant;
    __ addiw(as_Register($dst$$reg),
             as_Register($src1$$reg),
             $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

instruct addI_reg_imm_l2i(iRegINoSp dst, iRegL src1, immIAdd src2) %{
  match(Set dst (AddI (ConvL2I src1) src2));

  ins_cost(ALU_COST);
  format %{ "addiw  $dst, $src1, $src2\t#@addI_reg_imm_l2i" %}

  ins_encode %{
    __ addiw(as_Register($dst$$reg),
             as_Register($src1$$reg),
             $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

// Pointer Addition
instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{
  match(Set dst (AddP src1 src2));

  ins_cost(ALU_COST);
  format %{ "add $dst, $src1, $src2\t# ptr, #@addP_reg_reg" %}

  ins_encode %{
    __ add(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// If we shift more than 32 bits, we need not convert I2L.
instruct lShiftL_regI_immGE32(iRegLNoSp dst, iRegI src, uimmI6_ge32 scale) %{
  match(Set dst (LShiftL (ConvI2L src) scale));
  ins_cost(ALU_COST);
  format %{ "slli  $dst, $src, $scale & 63\t#@lShiftL_regI_immGE32" %}

  ins_encode %{
    __ slli(as_Register($dst$$reg), as_Register($src$$reg), $scale$$constant & 63);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Pointer Immediate Addition
// n.b. this needs to be more expensive than using an indirect memory
// operand
instruct addP_reg_imm(iRegPNoSp dst, iRegP src1, immLAdd src2) %{
  match(Set dst (AddP src1 src2));
  ins_cost(ALU_COST);
  format %{ "addi  $dst, $src1, $src2\t# ptr, #@addP_reg_imm" %}

  ins_encode %{
    // src2 is imm, so actually call the addi
    __ add(as_Register($dst$$reg),
           as_Register($src1$$reg),
           $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

// Long Addition
instruct addL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (AddL src1 src2));
  ins_cost(ALU_COST);
  format %{ "add  $dst, $src1, $src2\t#@addL_reg_reg" %}

  ins_encode %{
    __ add(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// No constant pool entries requiredLong Immediate Addition.
instruct addL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{
  match(Set dst (AddL src1 src2));
  ins_cost(ALU_COST);
  format %{ "addi  $dst, $src1, $src2\t#@addL_reg_imm" %}

  ins_encode %{
    // src2 is imm, so actually call the addi
    __ add(as_Register($dst$$reg),
           as_Register($src1$$reg),
           $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

// Integer Subtraction
instruct subI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (SubI src1 src2));

  ins_cost(ALU_COST);
  format %{ "subw  $dst, $src1, $src2\t#@subI_reg_reg" %}

  ins_encode %{
    __ subw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate Subtraction
instruct subI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immISub src2) %{
  match(Set dst (SubI src1 src2));

  ins_cost(ALU_COST);
  format %{ "addiw  $dst, $src1, -$src2\t#@subI_reg_imm" %}

  ins_encode %{
    // src2 is imm, so actually call the addiw
    __ subw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

// Long Subtraction
instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (SubL src1 src2));
  ins_cost(ALU_COST);
  format %{ "sub  $dst, $src1, $src2\t#@subL_reg_reg" %}

  ins_encode %{
    __ sub(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// No constant pool entries requiredLong Immediate Subtraction.
instruct subL_reg_imm(iRegLNoSp dst, iRegL src1, immLSub src2) %{
  match(Set dst (SubL src1 src2));
  ins_cost(ALU_COST);
  format %{ "addi  $dst, $src1, -$src2\t#@subL_reg_imm" %}

  ins_encode %{
    // src2 is imm, so actually call the addi
    __ sub(as_Register($dst$$reg),
           as_Register($src1$$reg),
           $src2$$constant);
  %}

  ins_pipe(ialu_reg_imm);
%}

// Integer Negation (special case for sub)

instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI0 zero) %{
  match(Set dst (SubI zero src));
  ins_cost(ALU_COST);
  format %{ "subw  $dst, x0, $src\t# int, #@negI_reg" %}

  ins_encode %{
    // actually call the subw
    __ negw(as_Register($dst$$reg),
            as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

// Long Negation

instruct negL_reg(iRegLNoSp dst, iRegL src, immL0 zero) %{
  match(Set dst (SubL zero src));
  ins_cost(ALU_COST);
  format %{ "sub  $dst, x0, $src\t# long, #@negL_reg" %}

  ins_encode %{
    // actually call the sub
    __ neg(as_Register($dst$$reg),
           as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

// Integer Multiply

instruct mulI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (MulI src1 src2));
  ins_cost(IMUL_COST);
  format %{ "mulw  $dst, $src1, $src2\t#@mulI" %}

  //this means 2 word multi, and no sign extend to 64 bits
  ins_encode %{
    // riscv64 mulw will sign-extension to high 32 bits in dst reg
    __ mulw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(imul_reg_reg);
%}

// Long Multiply

instruct mulL(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (MulL src1 src2));
  ins_cost(IMUL_COST);
  format %{ "mul  $dst, $src1, $src2\t#@mulL" %}

  ins_encode %{
    __ mul(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(lmul_reg_reg);
%}

instruct mulHiL_rReg(iRegLNoSp dst, iRegL src1, iRegL src2)
%{
  match(Set dst (MulHiL src1 src2));
  ins_cost(IMUL_COST);
  format %{ "mulh  $dst, $src1, $src2\t# mulhi, #@mulHiL_rReg" %}

  ins_encode %{
    __ mulh(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(lmul_reg_reg);
%}

// Integer Divide

instruct divI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (DivI src1 src2));
  ins_cost(IDIVSI_COST);
  format %{ "divw  $dst, $src1, $src2\t#@divI"%}

  ins_encode(riscv_enc_divw(dst, src1, src2));
  ins_pipe(idiv_reg_reg);
%}

instruct signExtract(iRegINoSp dst, iRegIorL2I src1, immI_31 div1, immI_31 div2) %{
  match(Set dst (URShiftI (RShiftI src1 div1) div2));
  ins_cost(ALU_COST);
  format %{ "srliw $dst, $src1, $div1\t# int signExtract, #@signExtract" %}

  ins_encode %{
    __ srliw(as_Register($dst$$reg), as_Register($src1$$reg), 31);
  %}
  ins_pipe(ialu_reg_shift);
%}

// Long Divide

instruct divL(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (DivL src1 src2));
  ins_cost(IDIVDI_COST);
  format %{ "div  $dst, $src1, $src2\t#@divL" %}

  ins_encode(riscv_enc_div(dst, src1, src2));
  ins_pipe(ldiv_reg_reg);
%}

instruct signExtractL(iRegLNoSp dst, iRegL src1, immI_63 div1, immI_63 div2) %{
  match(Set dst (URShiftL (RShiftL src1 div1) div2));
  ins_cost(ALU_COST);
  format %{ "srli $dst, $src1, $div1\t# long signExtract, #@signExtractL" %}

  ins_encode %{
    __ srli(as_Register($dst$$reg), as_Register($src1$$reg), 63);
  %}
  ins_pipe(ialu_reg_shift);
%}

// Integer Remainder

instruct modI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (ModI src1 src2));
  ins_cost(IDIVSI_COST);
  format %{ "remw  $dst, $src1, $src2\t#@modI" %}

  ins_encode(riscv_enc_modw(dst, src1, src2));
  ins_pipe(ialu_reg_reg);
%}

// Long Remainder

instruct modL(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (ModL src1 src2));
  ins_cost(IDIVDI_COST);
  format %{ "rem  $dst, $src1, $src2\t#@modL" %}

  ins_encode(riscv_enc_mod(dst, src1, src2));
  ins_pipe(ialu_reg_reg);
%}

// Integer Shifts

// Shift Left Register
// In RV64I, only the low 5 bits of src2 are considered for the shift amount
instruct lShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (LShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "sllw  $dst, $src1, $src2\t#@lShiftI_reg_reg" %}

  ins_encode %{
    __ sllw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Left Immediate
instruct lShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{
  match(Set dst (LShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "slliw  $dst, $src1, ($src2 & 0x1f)\t#@lShiftI_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 5 bits of the I-immediate field for RV32I
    __ slliw(as_Register($dst$$reg),
             as_Register($src1$$reg),
             (unsigned) $src2$$constant & 0x1f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Shift Right Logical Register
// In RV64I, only the low 5 bits of src2 are considered for the shift amount
instruct urShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (URShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "srlw  $dst, $src1, $src2\t#@urShiftI_reg_reg" %}

  ins_encode %{
    __ srlw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Right Logical Immediate
instruct urShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{
  match(Set dst (URShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "srliw  $dst, $src1, ($src2 & 0x1f)\t#@urShiftI_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 6 bits of the I-immediate field for RV64I
    __ srliw(as_Register($dst$$reg),
             as_Register($src1$$reg),
             (unsigned) $src2$$constant & 0x1f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Shift Right Arithmetic Register
// In RV64I, only the low 5 bits of src2 are considered for the shift amount
instruct rShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{
  match(Set dst (RShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "sraw  $dst, $src1, $src2\t#@rShiftI_reg_reg" %}

  ins_encode %{
    // riscv will sign-ext dst high 32 bits
    __ sraw(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Right Arithmetic Immediate
instruct rShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{
  match(Set dst (RShiftI src1 src2));
  ins_cost(ALU_COST);
  format %{ "sraiw  $dst, $src1, ($src2 & 0x1f)\t#@rShiftI_reg_imm" %}

  ins_encode %{
    // riscv will sign-ext dst high 32 bits
    __ sraiw(as_Register($dst$$reg),
             as_Register($src1$$reg),
             (unsigned) $src2$$constant & 0x1f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Long Shifts

// Shift Left Register
// In RV64I, only the low 6 bits of src2 are considered for the shift amount
instruct lShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{
  match(Set dst (LShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "sll  $dst, $src1, $src2\t#@lShiftL_reg_reg" %}

  ins_encode %{
    __ sll(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Left Immediate
instruct lShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{
  match(Set dst (LShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "slli  $dst, $src1, ($src2 & 0x3f)\t#@lShiftL_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 6 bits of the I-immediate field for RV64I
    __ slli(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (unsigned) $src2$$constant & 0x3f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Shift Right Logical Register
// In RV64I, only the low 6 bits of src2 are considered for the shift amount
instruct urShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{
  match(Set dst (URShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "srl  $dst, $src1, $src2\t#@urShiftL_reg_reg" %}

  ins_encode %{
    __ srl(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Right Logical Immediate
instruct urShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{
  match(Set dst (URShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "srli  $dst, $src1, ($src2 & 0x3f)\t#@urShiftL_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 6 bits of the I-immediate field for RV64I
    __ srli(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (unsigned) $src2$$constant & 0x3f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// A special-case pattern for card table stores.
instruct urShiftP_reg_imm(iRegLNoSp dst, iRegP src1, immI src2) %{
  match(Set dst (URShiftL (CastP2X src1) src2));

  ins_cost(ALU_COST);
  format %{ "srli  $dst, p2x($src1), ($src2 & 0x3f)\t#@urShiftP_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 6 bits of the I-immediate field for RV64I
    __ srli(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (unsigned) $src2$$constant & 0x3f);
  %}

  ins_pipe(ialu_reg_shift);
%}

// Shift Right Arithmetic Register
// In RV64I, only the low 6 bits of src2 are considered for the shift amount
instruct rShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{
  match(Set dst (RShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "sra  $dst, $src1, $src2\t#@rShiftL_reg_reg" %}

  ins_encode %{
    __ sra(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg_vshift);
%}

// Shift Right Arithmetic Immediate
instruct rShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{
  match(Set dst (RShiftL src1 src2));

  ins_cost(ALU_COST);
  format %{ "srai  $dst, $src1, ($src2 & 0x3f)\t#@rShiftL_reg_imm" %}

  ins_encode %{
    // the shift amount is encoded in the lower
    // 6 bits of the I-immediate field for RV64I
    __ srai(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (unsigned) $src2$$constant & 0x3f);
  %}

  ins_pipe(ialu_reg_shift);
%}

instruct regI_not_reg(iRegINoSp dst, iRegI src1, immI_M1 m1) %{
  match(Set dst (XorI src1 m1));
  ins_cost(ALU_COST);
  format %{ "xori  $dst, $src1, -1\t#@regI_not_reg" %}

  ins_encode %{
    __ xori(as_Register($dst$$reg), as_Register($src1$$reg), -1);
  %}

  ins_pipe(ialu_reg);
%}

instruct regL_not_reg(iRegLNoSp dst, iRegL src1, immL_M1 m1) %{
  match(Set dst (XorL src1 m1));
  ins_cost(ALU_COST);
  format %{ "xori  $dst, $src1, -1\t#@regL_not_reg" %}

  ins_encode %{
    __ xori(as_Register($dst$$reg), as_Register($src1$$reg), -1);
  %}

  ins_pipe(ialu_reg);
%}


// ============================================================================
// Floating Point Arithmetic Instructions

instruct addF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{
  match(Set dst (AddF src1 src2));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fadd.s  $dst, $src1, $src2\t#@addF_reg_reg" %}

  ins_encode %{
    __ fadd_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_s);
%}

instruct addD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{
  match(Set dst (AddD src1 src2));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fadd.d  $dst, $src1, $src2\t#@addD_reg_reg" %}

  ins_encode %{
    __ fadd_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_d);
%}

instruct subF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{
  match(Set dst (SubF src1 src2));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fsub.s  $dst, $src1, $src2\t#@subF_reg_reg" %}

  ins_encode %{
    __ fsub_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_s);
%}

instruct subD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{
  match(Set dst (SubD src1 src2));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fsub.d  $dst, $src1, $src2\t#@subD_reg_reg" %}

  ins_encode %{
    __ fsub_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_d);
%}

instruct mulF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{
  match(Set dst (MulF src1 src2));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fmul.s  $dst, $src1, $src2\t#@mulF_reg_reg" %}

  ins_encode %{
    __ fmul_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_s);
%}

instruct mulD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{
  match(Set dst (MulD src1 src2));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fmul.d  $dst, $src1, $src2\t#@mulD_reg_reg" %}

  ins_encode %{
    __ fmul_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_dop_reg_reg_d);
%}

// src1 * src2 + src3
instruct maddF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{
  predicate(UseFMA);
  match(Set dst (FmaF src3 (Binary src1 src2)));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fmadd.s  $dst, $src1, $src2, $src3\t#@maddF_reg_reg" %}

  ins_encode %{
    __ fmadd_s(as_FloatRegister($dst$$reg),
               as_FloatRegister($src1$$reg),
               as_FloatRegister($src2$$reg),
               as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// src1 * src2 + src3
instruct maddD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{
  predicate(UseFMA);
  match(Set dst (FmaD src3 (Binary src1 src2)));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fmadd.d  $dst, $src1, $src2, $src3\t#@maddD_reg_reg" %}

  ins_encode %{
    __ fmadd_d(as_FloatRegister($dst$$reg),
               as_FloatRegister($src1$$reg),
               as_FloatRegister($src2$$reg),
               as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// src1 * src2 - src3
instruct msubF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{
  predicate(UseFMA);
  match(Set dst (FmaF (NegF src3) (Binary src1 src2)));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fmsub.s  $dst, $src1, $src2, $src3\t#@msubF_reg_reg" %}

  ins_encode %{
    __ fmsub_s(as_FloatRegister($dst$$reg),
               as_FloatRegister($src1$$reg),
               as_FloatRegister($src2$$reg),
               as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// src1 * src2 - src3
instruct msubD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{
  predicate(UseFMA);
  match(Set dst (FmaD (NegD src3) (Binary src1 src2)));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fmsub.d  $dst, $src1, $src2, $src3\t#@msubD_reg_reg" %}

  ins_encode %{
    __ fmsub_d(as_FloatRegister($dst$$reg),
               as_FloatRegister($src1$$reg),
               as_FloatRegister($src2$$reg),
               as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// -src1 * src2 + src3
instruct nmsubF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{
  predicate(UseFMA);
  match(Set dst (FmaF src3 (Binary (NegF src1) src2)));
  match(Set dst (FmaF src3 (Binary src1 (NegF src2))));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fnmsub.s  $dst, $src1, $src2, $src3\t#@nmsubF_reg_reg" %}

  ins_encode %{
    __ fnmsub_s(as_FloatRegister($dst$$reg),
                as_FloatRegister($src1$$reg),
                as_FloatRegister($src2$$reg),
                as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// -src1 * src2 + src3
instruct nmsubD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{
  predicate(UseFMA);
  match(Set dst (FmaD src3 (Binary (NegD src1) src2)));
  match(Set dst (FmaD src3 (Binary src1 (NegD src2))));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fnmsub.d  $dst, $src1, $src2, $src3\t#@nmsubD_reg_reg" %}

  ins_encode %{
    __ fnmsub_d(as_FloatRegister($dst$$reg),
                as_FloatRegister($src1$$reg),
                as_FloatRegister($src2$$reg),
                as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// -src1 * src2 - src3
instruct nmaddF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{
  predicate(UseFMA);
  match(Set dst (FmaF (NegF src3) (Binary (NegF src1) src2)));
  match(Set dst (FmaF (NegF src3) (Binary src1 (NegF src2))));

  ins_cost(FMUL_SINGLE_COST);
  format %{ "fnmadd.s  $dst, $src1, $src2, $src3\t#@nmaddF_reg_reg" %}

  ins_encode %{
    __ fnmadd_s(as_FloatRegister($dst$$reg),
                as_FloatRegister($src1$$reg),
                as_FloatRegister($src2$$reg),
                as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// -src1 * src2 - src3
instruct nmaddD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{
  predicate(UseFMA);
  match(Set dst (FmaD (NegD src3) (Binary (NegD src1) src2)));
  match(Set dst (FmaD (NegD src3) (Binary src1 (NegD src2))));

  ins_cost(FMUL_DOUBLE_COST);
  format %{ "fnmadd.d  $dst, $src1, $src2, $src3\t#@nmaddD_reg_reg" %}

  ins_encode %{
    __ fnmadd_d(as_FloatRegister($dst$$reg),
                as_FloatRegister($src1$$reg),
                as_FloatRegister($src2$$reg),
                as_FloatRegister($src3$$reg));
  %}

  ins_pipe(pipe_class_default);
%}

// Math.max(FF)F
instruct maxF_reg_reg(fRegF dst, fRegF src1, fRegF src2, rFlagsReg cr) %{
  match(Set dst (MaxF src1 src2));
  effect(TEMP_DEF dst, KILL cr);

  format %{ "maxF $dst, $src1, $src2" %}

  ins_encode %{
    __ minmax_FD(as_FloatRegister($dst$$reg),
                 as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg),
                 false /* is_double */, false /* is_min */);
  %}

  ins_pipe(fp_dop_reg_reg_s);
%}

// Math.min(FF)F
instruct minF_reg_reg(fRegF dst, fRegF src1, fRegF src2, rFlagsReg cr) %{
  match(Set dst (MinF src1 src2));
  effect(TEMP_DEF dst, KILL cr);

  format %{ "minF $dst, $src1, $src2" %}

  ins_encode %{
    __ minmax_FD(as_FloatRegister($dst$$reg),
                 as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg),
                 false /* is_double */, true /* is_min */);
  %}

  ins_pipe(fp_dop_reg_reg_s);
%}

// Math.max(DD)D
instruct maxD_reg_reg(fRegD dst, fRegD src1, fRegD src2, rFlagsReg cr) %{
  match(Set dst (MaxD src1 src2));
  effect(TEMP_DEF dst, KILL cr);

  format %{ "maxD $dst, $src1, $src2" %}

  ins_encode %{
    __ minmax_FD(as_FloatRegister($dst$$reg),
                 as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg),
                 true /* is_double */, false /* is_min */);
  %}

  ins_pipe(fp_dop_reg_reg_d);
%}

// Math.min(DD)D
instruct minD_reg_reg(fRegD dst, fRegD src1, fRegD src2, rFlagsReg cr) %{
  match(Set dst (MinD src1 src2));
  effect(TEMP_DEF dst, KILL cr);

  format %{ "minD $dst, $src1, $src2" %}

  ins_encode %{
    __ minmax_FD(as_FloatRegister($dst$$reg),
                 as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg),
                 true /* is_double */, true /* is_min */);
  %}

  ins_pipe(fp_dop_reg_reg_d);
%}

// Float.isInfinite
instruct isIniniteF_reg_reg(iRegINoSp dst, fRegF src)
%{
  match(Set dst (IsInfiniteF src));
  format %{ "isInfinite $dst, $src" %}
  ins_encode %{
    __ fclass_s(as_Register($dst$$reg), as_FloatRegister($src$$reg));
    __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b10000001);
    __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg));
  %}
  ins_pipe(fp_dop_reg_reg_s);
%}

// Double.isInfinite
instruct isInfiniteD_reg_reg(iRegINoSp dst, fRegD src)
%{
  match(Set dst (IsInfiniteD src));
  format %{ "isInfinite $dst, $src" %}
  ins_encode %{
    __ fclass_d(as_Register($dst$$reg), as_FloatRegister($src$$reg));
    __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b10000001);
    __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg));
  %}
  ins_pipe(fp_dop_reg_reg_d);
%}

// Float.isFinite
instruct isFiniteF_reg_reg(iRegINoSp dst, fRegF src)
%{
  match(Set dst (IsFiniteF src));
  format %{ "isFinite $dst, $src" %}
  ins_encode %{
    __ fclass_s(as_Register($dst$$reg), as_FloatRegister($src$$reg));
    __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0001111110);
    __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg));
  %}
  ins_pipe(fp_dop_reg_reg_s);
%}

// Double.isFinite
instruct isFiniteD_reg_reg(iRegINoSp dst, fRegD src)
%{
  match(Set dst (IsFiniteD src));
  format %{ "isFinite $dst, $src" %}
  ins_encode %{
    __ fclass_d(as_Register($dst$$reg), as_FloatRegister($src$$reg));
    __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0001111110);
    __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg));
  %}
  ins_pipe(fp_dop_reg_reg_d);
%}

instruct divF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{
  match(Set dst (DivF src1  src2));

  ins_cost(FDIV_COST);
  format %{ "fdiv.s  $dst, $src1, $src2\t#@divF_reg_reg" %}

  ins_encode %{
    __ fdiv_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_div_s);
%}

instruct divD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{
  match(Set dst (DivD src1  src2));

  ins_cost(FDIV_COST);
  format %{ "fdiv.d  $dst, $src1, $src2\t#@divD_reg_reg" %}

  ins_encode %{
    __ fdiv_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src1$$reg),
              as_FloatRegister($src2$$reg));
  %}

  ins_pipe(fp_div_d);
%}

instruct negF_reg_reg(fRegF dst, fRegF src) %{
  match(Set dst (NegF src));

  ins_cost(XFER_COST);
  format %{ "fsgnjn.s  $dst, $src, $src\t#@negF_reg_reg" %}

  ins_encode %{
    __ fneg_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_uop_s);
%}

instruct negD_reg_reg(fRegD dst, fRegD src) %{
  match(Set dst (NegD src));

  ins_cost(XFER_COST);
  format %{ "fsgnjn.d  $dst, $src, $src\t#@negD_reg_reg" %}

  ins_encode %{
    __ fneg_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_uop_d);
%}

instruct absI_reg(iRegINoSp dst, iRegIorL2I src) %{
  match(Set dst (AbsI src));

  ins_cost(ALU_COST * 3);
  format %{
    "sraiw  t0, $src, 0x1f\n\t"
    "addw  $dst, $src, t0\n\t"
    "xorr  $dst, $dst, t0\t#@absI_reg"
  %}

  ins_encode %{
    __ sraiw(t0, as_Register($src$$reg), 0x1f);
    __ addw(as_Register($dst$$reg), as_Register($src$$reg), t0);
    __ xorr(as_Register($dst$$reg), as_Register($dst$$reg), t0);
  %}

  ins_pipe(ialu_reg_reg);
%}

instruct absL_reg(iRegLNoSp dst, iRegL src) %{
  match(Set dst (AbsL src));

  ins_cost(ALU_COST * 3);
  format %{
    "srai  t0, $src, 0x3f\n\t"
    "add  $dst, $src, t0\n\t"
    "xorr  $dst, $dst, t0\t#@absL_reg"
  %}

  ins_encode %{
    __ srai(t0, as_Register($src$$reg), 0x3f);
    __ add(as_Register($dst$$reg), as_Register($src$$reg), t0);
    __ xorr(as_Register($dst$$reg), as_Register($dst$$reg), t0);
  %}

  ins_pipe(ialu_reg_reg);
%}

instruct absF_reg(fRegF dst, fRegF src) %{
  match(Set dst (AbsF src));

  ins_cost(XFER_COST);
  format %{ "fsgnjx.s  $dst, $src, $src\t#@absF_reg" %}
  ins_encode %{
    __ fabs_s(as_FloatRegister($dst$$reg),
              as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_uop_s);
%}

instruct absD_reg(fRegD dst, fRegD src) %{
  match(Set dst (AbsD src));

  ins_cost(XFER_COST);
  format %{ "fsgnjx.d  $dst, $src, $src\t#@absD_reg" %}
  ins_encode %{
    __ fabs_d(as_FloatRegister($dst$$reg),
              as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_uop_d);
%}

instruct sqrtF_reg(fRegF dst, fRegF src) %{
  match(Set dst (ConvD2F (SqrtD (ConvF2D src))));

  ins_cost(FSQRT_COST);
  format %{ "fsqrt.s  $dst, $src\t#@sqrtF_reg" %}
  ins_encode %{
    __ fsqrt_s(as_FloatRegister($dst$$reg),
               as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_sqrt_s);
%}

instruct sqrtD_reg(fRegD dst, fRegD src) %{
  match(Set dst (SqrtD src));

  ins_cost(FSQRT_COST);
  format %{ "fsqrt.d  $dst, $src\t#@sqrtD_reg" %}
  ins_encode %{
    __ fsqrt_d(as_FloatRegister($dst$$reg),
               as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_sqrt_d);
%}

// Arithmetic Instructions End

// ============================================================================
// Logical Instructions

// Register And
instruct andI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{
  match(Set dst (AndI src1 src2));

  format %{ "andr  $dst, $src1, $src2\t#@andI_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ andr(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate And
instruct andI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{
  match(Set dst (AndI src1 src2));

  format %{ "andi  $dst, $src1, $src2\t#@andI_reg_imm" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ andi(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// Register Or
instruct orI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{
  match(Set dst (OrI src1 src2));

  format %{ "orr  $dst, $src1, $src2\t#@orI_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ orr(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate Or
instruct orI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{
  match(Set dst (OrI src1 src2));

  format %{ "ori  $dst, $src1, $src2\t#@orI_reg_imm" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ ori(as_Register($dst$$reg),
           as_Register($src1$$reg),
           (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// Register Xor
instruct xorI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{
  match(Set dst (XorI src1 src2));

  format %{ "xorr  $dst, $src1, $src2\t#@xorI_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ xorr(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate Xor
instruct xorI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{
  match(Set dst (XorI src1 src2));

  format %{ "xori  $dst, $src1, $src2\t#@xorI_reg_imm" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ xori(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// Register And Long
instruct andL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (AndL src1 src2));

  format %{ "andr  $dst, $src1, $src2\t#@andL_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ andr(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate And Long
instruct andL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{
  match(Set dst (AndL src1 src2));

  format %{ "andi  $dst, $src1, $src2\t#@andL_reg_imm" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ andi(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// Register Or Long
instruct orL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (OrL src1 src2));

  format %{ "orr  $dst, $src1, $src2\t#@orL_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ orr(as_Register($dst$$reg),
           as_Register($src1$$reg),
           as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate Or Long
instruct orL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{
  match(Set dst (OrL src1 src2));

  format %{ "ori  $dst, $src1, $src2\t#@orL_reg_imm" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ ori(as_Register($dst$$reg),
           as_Register($src1$$reg),
           (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// Register Xor Long
instruct xorL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{
  match(Set dst (XorL src1 src2));

  format %{ "xorr  $dst, $src1, $src2\t#@xorL_reg_reg" %}

  ins_cost(ALU_COST);
  ins_encode %{
    __ xorr(as_Register($dst$$reg),
            as_Register($src1$$reg),
            as_Register($src2$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

// Immediate Xor Long
instruct xorL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{
  match(Set dst (XorL src1 src2));

  ins_cost(ALU_COST);
  format %{ "xori  $dst, $src1, $src2\t#@xorL_reg_imm" %}

  ins_encode %{
    __ xori(as_Register($dst$$reg),
            as_Register($src1$$reg),
            (int32_t)($src2$$constant));
  %}

  ins_pipe(ialu_reg_imm);
%}

// ============================================================================
// BSWAP Instructions

instruct bytes_reverse_int(iRegINoSp dst, iRegIorL2I src, rFlagsReg cr) %{
  match(Set dst (ReverseBytesI src));
  effect(TEMP cr);

  ins_cost(ALU_COST * 13);
  format %{ "revb_w_w  $dst, $src\t#@bytes_reverse_int" %}

  ins_encode %{
    __ revb_w_w(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

instruct bytes_reverse_long(iRegLNoSp dst, iRegL src, rFlagsReg cr) %{
  match(Set dst (ReverseBytesL src));
  effect(TEMP cr);

  ins_cost(ALU_COST * 29);
  format %{ "revb  $dst, $src\t#@bytes_reverse_long" %}

  ins_encode %{
    __ revb(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

instruct bytes_reverse_unsigned_short(iRegINoSp dst, iRegIorL2I src) %{
  match(Set dst (ReverseBytesUS src));

  ins_cost(ALU_COST * 5);
  format %{ "revb_h_h_u  $dst, $src\t#@bytes_reverse_unsigned_short" %}

  ins_encode %{
    __ revb_h_h_u(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

instruct bytes_reverse_short(iRegINoSp dst, iRegIorL2I src) %{
  match(Set dst (ReverseBytesS src));

  ins_cost(ALU_COST * 5);
  format %{ "revb_h_h  $dst, $src\t#@bytes_reverse_short" %}

  ins_encode %{
    __ revb_h_h(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

// ============================================================================
// MemBar Instruction

instruct load_fence() %{
  match(LoadFence);
  ins_cost(ALU_COST);

  format %{ "#@load_fence" %}

  ins_encode %{
    __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
  %}
  ins_pipe(pipe_serial);
%}

instruct membar_acquire() %{
  match(MemBarAcquire);
  ins_cost(ALU_COST);

  format %{ "#@membar_acquire\n\t"
            "fence ir iorw" %}

  ins_encode %{
    __ block_comment("membar_acquire");
    __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
  %}

  ins_pipe(pipe_serial);
%}

instruct membar_acquire_lock() %{
  match(MemBarAcquireLock);
  ins_cost(0);

  format %{ "#@membar_acquire_lock (elided)" %}

  ins_encode %{
    __ block_comment("membar_acquire_lock (elided)");
  %}

  ins_pipe(pipe_serial);
%}

instruct store_fence() %{
  match(StoreFence);
  ins_cost(ALU_COST);

  format %{ "#@store_fence" %}

  ins_encode %{
    __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
  %}
  ins_pipe(pipe_serial);
%}

instruct membar_release() %{
  match(MemBarRelease);
  ins_cost(ALU_COST);

  format %{ "#@membar_release\n\t"
            "fence iorw ow" %}

  ins_encode %{
    __ block_comment("membar_release");
    __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
  %}
  ins_pipe(pipe_serial);
%}

instruct membar_storestore() %{
  match(MemBarStoreStore);
  match(StoreStoreFence);
  ins_cost(ALU_COST);

  format %{ "MEMBAR-store-store\t#@membar_storestore" %}

  ins_encode %{
    __ membar(MacroAssembler::StoreStore);
  %}
  ins_pipe(pipe_serial);
%}

instruct membar_release_lock() %{
  match(MemBarReleaseLock);
  ins_cost(0);

  format %{ "#@membar_release_lock (elided)" %}

  ins_encode %{
    __ block_comment("membar_release_lock (elided)");
  %}

  ins_pipe(pipe_serial);
%}

instruct membar_volatile() %{
  match(MemBarVolatile);
  ins_cost(ALU_COST);

  format %{ "#@membar_volatile\n\t"
             "fence iorw iorw"%}

  ins_encode %{
    __ block_comment("membar_volatile");
    __ membar(MacroAssembler::StoreLoad);
  %}

  ins_pipe(pipe_serial);
%}

// ============================================================================
// Cast Instructions (Java-level type cast)

instruct castX2P(iRegPNoSp dst, iRegL src) %{
  match(Set dst (CastX2P src));

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $src\t# long -> ptr, #@castX2P" %}

  ins_encode %{
    if ($dst$$reg != $src$$reg) {
      __ mv(as_Register($dst$$reg), as_Register($src$$reg));
    }
  %}

  ins_pipe(ialu_reg);
%}

instruct castP2X(iRegLNoSp dst, iRegP src) %{
  match(Set dst (CastP2X src));

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $src\t# ptr -> long, #@castP2X" %}

  ins_encode %{
    if ($dst$$reg != $src$$reg) {
      __ mv(as_Register($dst$$reg), as_Register($src$$reg));
    }
  %}

  ins_pipe(ialu_reg);
%}

instruct castPP(iRegPNoSp dst)
%{
  match(Set dst (CastPP dst));
  ins_cost(0);

  size(0);
  format %{ "# castPP of $dst, #@castPP" %}
  ins_encode(/* empty encoding */);
  ins_pipe(pipe_class_empty);
%}

instruct castLL(iRegL dst)
%{
  match(Set dst (CastLL dst));

  size(0);
  format %{ "# castLL of $dst, #@castLL" %}
  ins_encode(/* empty encoding */);
  ins_cost(0);
  ins_pipe(pipe_class_empty);
%}

instruct castII(iRegI dst)
%{
  match(Set dst (CastII dst));

  size(0);
  format %{ "# castII of $dst, #@castII" %}
  ins_encode(/* empty encoding */);
  ins_cost(0);
  ins_pipe(pipe_class_empty);
%}

instruct checkCastPP(iRegPNoSp dst)
%{
  match(Set dst (CheckCastPP dst));

  size(0);
  ins_cost(0);
  format %{ "# checkcastPP of $dst, #@checkCastPP" %}
  ins_encode(/* empty encoding */);
  ins_pipe(pipe_class_empty);
%}

instruct castFF(fRegF dst)
%{
  match(Set dst (CastFF dst));

  size(0);
  format %{ "# castFF of $dst" %}
  ins_encode(/* empty encoding */);
  ins_cost(0);
  ins_pipe(pipe_class_empty);
%}

instruct castDD(fRegD dst)
%{
  match(Set dst (CastDD dst));

  size(0);
  format %{ "# castDD of $dst" %}
  ins_encode(/* empty encoding */);
  ins_cost(0);
  ins_pipe(pipe_class_empty);
%}

instruct castVV(vReg dst)
%{
  match(Set dst (CastVV dst));

  size(0);
  format %{ "# castVV of $dst" %}
  ins_encode(/* empty encoding */);
  ins_cost(0);
  ins_pipe(pipe_class_empty);
%}

// ============================================================================
// Convert Instructions

// int to bool
instruct convI2Bool(iRegINoSp dst, iRegI src)
%{
  match(Set dst (Conv2B src));

  ins_cost(ALU_COST);
  format %{ "snez  $dst, $src\t#@convI2Bool" %}

  ins_encode %{
    __ snez(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

// pointer to bool
instruct convP2Bool(iRegINoSp dst, iRegP src)
%{
  match(Set dst (Conv2B src));

  ins_cost(ALU_COST);
  format %{ "snez  $dst, $src\t#@convP2Bool" %}

  ins_encode %{
    __ snez(as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(ialu_reg);
%}

// int <-> long

instruct convI2L_reg_reg(iRegLNoSp dst, iRegIorL2I src)
%{
  match(Set dst (ConvI2L src));

  ins_cost(ALU_COST);
  format %{ "addw  $dst, $src, zr\t#@convI2L_reg_reg" %}
  ins_encode %{
    __ addw(as_Register($dst$$reg), as_Register($src$$reg), zr);
  %}
  ins_pipe(ialu_reg);
%}

instruct convL2I_reg(iRegINoSp dst, iRegL src) %{
  match(Set dst (ConvL2I src));

  ins_cost(ALU_COST);
  format %{ "addw  $dst, $src, zr\t#@convL2I_reg" %}

  ins_encode %{
    __ addw(as_Register($dst$$reg), as_Register($src$$reg), zr);
  %}

  ins_pipe(ialu_reg);
%}

// int to unsigned long (Zero-extend)
instruct convI2UL_reg_reg(iRegLNoSp dst, iRegIorL2I src, immL_32bits mask)
%{
  match(Set dst (AndL (ConvI2L src) mask));

  ins_cost(ALU_COST * 2);
  format %{ "zero_extend $dst, $src, 32\t# i2ul, #@convI2UL_reg_reg" %}

  ins_encode %{
    __ zero_extend(as_Register($dst$$reg), as_Register($src$$reg), 32);
  %}

  ins_pipe(ialu_reg_shift);
%}

// float <-> double

instruct convF2D_reg(fRegD dst, fRegF src) %{
  match(Set dst (ConvF2D src));

  ins_cost(XFER_COST);
  format %{ "fcvt.d.s  $dst, $src\t#@convF2D_reg" %}

  ins_encode %{
    __ fcvt_d_s(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_f2d);
%}

instruct convD2F_reg(fRegF dst, fRegD src) %{
  match(Set dst (ConvD2F src));

  ins_cost(XFER_COST);
  format %{ "fcvt.s.d  $dst, $src\t#@convD2F_reg" %}

  ins_encode %{
    __ fcvt_s_d(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_d2f);
%}

// float <-> int

instruct convF2I_reg_reg(iRegINoSp dst, fRegF src) %{
  match(Set dst (ConvF2I src));

  ins_cost(XFER_COST);
  format %{ "fcvt.w.s  $dst, $src\t#@convF2I_reg_reg" %}

  ins_encode %{
    __ fcvt_w_s_safe($dst$$Register, $src$$FloatRegister);
  %}

  ins_pipe(fp_f2i);
%}

instruct convI2F_reg_reg(fRegF dst, iRegIorL2I src) %{
  match(Set dst (ConvI2F src));

  ins_cost(XFER_COST);
  format %{ "fcvt.s.w  $dst, $src\t#@convI2F_reg_reg" %}

  ins_encode %{
    __ fcvt_s_w(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_i2f);
%}

// float <-> long

instruct convF2L_reg_reg(iRegLNoSp dst, fRegF src) %{
  match(Set dst (ConvF2L src));

  ins_cost(XFER_COST);
  format %{ "fcvt.l.s  $dst, $src\t#@convF2L_reg_reg" %}

  ins_encode %{
    __ fcvt_l_s_safe($dst$$Register, $src$$FloatRegister);
  %}

  ins_pipe(fp_f2l);
%}

instruct convL2F_reg_reg(fRegF dst, iRegL src) %{
  match(Set dst (ConvL2F src));

  ins_cost(XFER_COST);
  format %{ "fcvt.s.l  $dst, $src\t#@convL2F_reg_reg" %}

  ins_encode %{
    __ fcvt_s_l(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_l2f);
%}

// double <-> int

instruct convD2I_reg_reg(iRegINoSp dst, fRegD src) %{
  match(Set dst (ConvD2I src));

  ins_cost(XFER_COST);
  format %{ "fcvt.w.d  $dst, $src\t#@convD2I_reg_reg" %}

  ins_encode %{
    __ fcvt_w_d_safe($dst$$Register, $src$$FloatRegister);
  %}

  ins_pipe(fp_d2i);
%}

instruct convI2D_reg_reg(fRegD dst, iRegIorL2I src) %{
  match(Set dst (ConvI2D src));

  ins_cost(XFER_COST);
  format %{ "fcvt.d.w  $dst, $src\t#@convI2D_reg_reg" %}

  ins_encode %{
    __ fcvt_d_w(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_i2d);
%}

// double <-> long

instruct convD2L_reg_reg(iRegLNoSp dst, fRegD src) %{
  match(Set dst (ConvD2L src));

  ins_cost(XFER_COST);
  format %{ "fcvt.l.d  $dst, $src\t#@convD2L_reg_reg" %}

  ins_encode %{
    __ fcvt_l_d_safe($dst$$Register, $src$$FloatRegister);
  %}

  ins_pipe(fp_d2l);
%}

instruct convL2D_reg_reg(fRegD dst, iRegL src) %{
  match(Set dst (ConvL2D src));

  ins_cost(XFER_COST);
  format %{ "fcvt.d.l  $dst, $src\t#@convL2D_reg_reg" %}

  ins_encode %{
    __ fcvt_d_l(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_l2d);
%}

// Convert oop into int for vectors alignment masking
instruct convP2I(iRegINoSp dst, iRegP src) %{
  match(Set dst (ConvL2I (CastP2X src)));

  ins_cost(ALU_COST * 2);
  format %{ "zero_extend $dst, $src, 32\t# ptr -> int, #@convP2I" %}

  ins_encode %{
    __ zero_extend($dst$$Register, $src$$Register, 32);
  %}

  ins_pipe(ialu_reg);
%}

// Convert compressed oop into int for vectors alignment masking
// in case of 32bit oops (heap < 4Gb).
instruct convN2I(iRegINoSp dst, iRegN src)
%{
  predicate(CompressedOops::shift() == 0);
  match(Set dst (ConvL2I (CastP2X (DecodeN src))));

  ins_cost(ALU_COST);
  format %{ "mv  $dst, $src\t# compressed ptr -> int, #@convN2I" %}

  ins_encode %{
    __ mv($dst$$Register, $src$$Register);
  %}

  ins_pipe(ialu_reg);
%}

// Convert oop pointer into compressed form
instruct encodeHeapOop(iRegNNoSp dst, iRegP src) %{
  match(Set dst (EncodeP src));
  ins_cost(ALU_COST);
  format %{ "encode_heap_oop  $dst, $src\t#@encodeHeapOop" %}
  ins_encode %{
    Register s = $src$$Register;
    Register d = $dst$$Register;
    __ encode_heap_oop(d, s);
  %}
  ins_pipe(ialu_reg);
%}

instruct decodeHeapOop(iRegPNoSp dst, iRegN src) %{
  predicate(n->bottom_type()->is_ptr()->ptr() != TypePtr::NotNull &&
            n->bottom_type()->is_ptr()->ptr() != TypePtr::Constant);
  match(Set dst (DecodeN src));

  ins_cost(0);
  format %{ "decode_heap_oop  $dst, $src\t#@decodeHeapOop" %}
  ins_encode %{
    Register s = $src$$Register;
    Register d = $dst$$Register;
    __ decode_heap_oop(d, s);
  %}
  ins_pipe(ialu_reg);
%}

instruct decodeHeapOop_not_null(iRegPNoSp dst, iRegN src) %{
  predicate(n->bottom_type()->is_ptr()->ptr() == TypePtr::NotNull ||
            n->bottom_type()->is_ptr()->ptr() == TypePtr::Constant);
  match(Set dst (DecodeN src));

  ins_cost(0);
  format %{ "decode_heap_oop_not_null $dst, $src\t#@decodeHeapOop_not_null" %}
  ins_encode %{
    Register s = $src$$Register;
    Register d = $dst$$Register;
    __ decode_heap_oop_not_null(d, s);
  %}
  ins_pipe(ialu_reg);
%}

// Convert klass pointer into compressed form.
instruct encodeKlass_not_null(iRegNNoSp dst, iRegP src) %{
  match(Set dst (EncodePKlass src));

  ins_cost(ALU_COST);
  format %{ "encode_klass_not_null  $dst, $src\t#@encodeKlass_not_null" %}

  ins_encode %{
    Register src_reg = as_Register($src$$reg);
    Register dst_reg = as_Register($dst$$reg);
    __ encode_klass_not_null(dst_reg, src_reg, t0);
  %}

   ins_pipe(ialu_reg);
%}

instruct decodeKlass_not_null(iRegPNoSp dst, iRegN src, iRegPNoSp tmp) %{
  match(Set dst (DecodeNKlass src));

  effect(TEMP tmp);

  ins_cost(ALU_COST);
  format %{ "decode_klass_not_null  $dst, $src\t#@decodeKlass_not_null" %}

  ins_encode %{
    Register src_reg = as_Register($src$$reg);
    Register dst_reg = as_Register($dst$$reg);
    Register tmp_reg = as_Register($tmp$$reg);
    __ decode_klass_not_null(dst_reg, src_reg, tmp_reg);
  %}

   ins_pipe(ialu_reg);
%}

// stack <-> reg and reg <-> reg shuffles with no conversion

instruct MoveF2I_stack_reg(iRegINoSp dst, stackSlotF src) %{

  match(Set dst (MoveF2I src));

  effect(DEF dst, USE src);

  ins_cost(LOAD_COST);

  format %{ "lw  $dst, $src\t#@MoveF2I_stack_reg" %}

  ins_encode %{
    __ lw(as_Register($dst$$reg), Address(sp, $src$$disp));
  %}

  ins_pipe(iload_reg_reg);

%}

instruct MoveI2F_stack_reg(fRegF dst, stackSlotI src) %{

  match(Set dst (MoveI2F src));

  effect(DEF dst, USE src);

  ins_cost(LOAD_COST);

  format %{ "flw  $dst, $src\t#@MoveI2F_stack_reg" %}

  ins_encode %{
    __ flw(as_FloatRegister($dst$$reg), Address(sp, $src$$disp));
  %}

  ins_pipe(pipe_class_memory);

%}

instruct MoveD2L_stack_reg(iRegLNoSp dst, stackSlotD src) %{

  match(Set dst (MoveD2L src));

  effect(DEF dst, USE src);

  ins_cost(LOAD_COST);

  format %{ "ld  $dst, $src\t#@MoveD2L_stack_reg" %}

  ins_encode %{
    __ ld(as_Register($dst$$reg), Address(sp, $src$$disp));
  %}

  ins_pipe(iload_reg_reg);

%}

instruct MoveL2D_stack_reg(fRegD dst, stackSlotL src) %{

  match(Set dst (MoveL2D src));

  effect(DEF dst, USE src);

  ins_cost(LOAD_COST);

  format %{ "fld  $dst, $src\t#@MoveL2D_stack_reg" %}

  ins_encode %{
    __ fld(as_FloatRegister($dst$$reg), Address(sp, $src$$disp));
  %}

  ins_pipe(pipe_class_memory);

%}

instruct MoveF2I_reg_stack(stackSlotI dst, fRegF src) %{

  match(Set dst (MoveF2I src));

  effect(DEF dst, USE src);

  ins_cost(STORE_COST);

  format %{ "fsw  $src, $dst\t#@MoveF2I_reg_stack" %}

  ins_encode %{
    __ fsw(as_FloatRegister($src$$reg), Address(sp, $dst$$disp));
  %}

  ins_pipe(pipe_class_memory);

%}

instruct MoveI2F_reg_stack(stackSlotF dst, iRegI src) %{

  match(Set dst (MoveI2F src));

  effect(DEF dst, USE src);

  ins_cost(STORE_COST);

  format %{ "sw  $src, $dst\t#@MoveI2F_reg_stack" %}

  ins_encode %{
    __ sw(as_Register($src$$reg), Address(sp, $dst$$disp));
  %}

  ins_pipe(istore_reg_reg);

%}

instruct MoveD2L_reg_stack(stackSlotL dst, fRegD src) %{

  match(Set dst (MoveD2L src));

  effect(DEF dst, USE src);

  ins_cost(STORE_COST);

  format %{ "fsd  $dst, $src\t#@MoveD2L_reg_stack" %}

  ins_encode %{
    __ fsd(as_FloatRegister($src$$reg), Address(sp, $dst$$disp));
  %}

  ins_pipe(pipe_class_memory);

%}

instruct MoveL2D_reg_stack(stackSlotD dst, iRegL src) %{

  match(Set dst (MoveL2D src));

  effect(DEF dst, USE src);

  ins_cost(STORE_COST);

  format %{ "sd  $src, $dst\t#@MoveL2D_reg_stack" %}

  ins_encode %{
    __ sd(as_Register($src$$reg), Address(sp, $dst$$disp));
  %}

  ins_pipe(istore_reg_reg);

%}

instruct MoveF2I_reg_reg(iRegINoSp dst, fRegF src) %{

  match(Set dst (MoveF2I src));

  effect(DEF dst, USE src);

  ins_cost(XFER_COST);

  format %{ "fmv.x.w  $dst, $src\t#@MoveL2D_reg_stack" %}

  ins_encode %{
    __ fmv_x_w(as_Register($dst$$reg), as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_f2i);

%}

instruct MoveI2F_reg_reg(fRegF dst, iRegI src) %{

  match(Set dst (MoveI2F src));

  effect(DEF dst, USE src);

  ins_cost(XFER_COST);

  format %{ "fmv.w.x  $dst, $src\t#@MoveI2F_reg_reg" %}

  ins_encode %{
    __ fmv_w_x(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_i2f);

%}

instruct MoveD2L_reg_reg(iRegLNoSp dst, fRegD src) %{

  match(Set dst (MoveD2L src));

  effect(DEF dst, USE src);

  ins_cost(XFER_COST);

  format %{ "fmv.x.d $dst, $src\t#@MoveD2L_reg_reg" %}

  ins_encode %{
    __ fmv_x_d(as_Register($dst$$reg), as_FloatRegister($src$$reg));
  %}

  ins_pipe(fp_d2l);

%}

instruct MoveL2D_reg_reg(fRegD dst, iRegL src) %{

  match(Set dst (MoveL2D src));

  effect(DEF dst, USE src);

  ins_cost(XFER_COST);

  format %{ "fmv.d.x  $dst, $src\t#@MoveD2L_reg_reg" %}

  ins_encode %{
    __ fmv_d_x(as_FloatRegister($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(fp_l2d);
%}

// ============================================================================
// Compare Instructions which set the result float comparisons in dest register.

instruct cmpF3_reg_reg(iRegINoSp dst, fRegF op1, fRegF op2)
%{
  match(Set dst (CmpF3 op1 op2));

  ins_cost(XFER_COST * 2 + BRANCH_COST + ALU_COST);
  format %{ "flt.s  $dst, $op2, $op1\t#@cmpF3_reg_reg\n\t"
            "bgtz   $dst, done\n\t"
            "feq.s  $dst, $op1, $op2\n\t"
            "addi   $dst, $dst, -1\t#@cmpF3_reg_reg"
  %}

  ins_encode %{
    // we want -1 for unordered or less than, 0 for equal and 1 for greater than.
    __ float_compare(as_Register($dst$$reg), as_FloatRegister($op1$$reg),
                     as_FloatRegister($op2$$reg), -1 /*unordered_result < 0*/);
  %}

  ins_pipe(pipe_class_default);
%}

instruct cmpD3_reg_reg(iRegINoSp dst, fRegD op1, fRegD op2)
%{
  match(Set dst (CmpD3 op1 op2));

  ins_cost(XFER_COST * 2 + BRANCH_COST + ALU_COST);
  format %{ "flt.d  $dst, $op2, $op1\t#@cmpD3_reg_reg\n\t"
            "bgtz   $dst, done\n\t"
            "feq.d  $dst, $op1, $op2\n\t"
            "addi   $dst, $dst, -1\t#@cmpD3_reg_reg"
  %}

  ins_encode %{
    // we want -1 for unordered or less than, 0 for equal and 1 for greater than.
    __ double_compare(as_Register($dst$$reg), as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), -1 /*unordered_result < 0*/);
  %}

  ins_pipe(pipe_class_default);
%}

instruct cmpL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2)
%{
  match(Set dst (CmpL3 op1 op2));

  ins_cost(ALU_COST * 3 + BRANCH_COST);
  format %{ "slt   $dst, $op2, $op1\t#@cmpL3_reg_reg\n\t"
            "bnez  $dst, done\n\t"
            "slt  $dst, $op1, $op2\n\t"
            "neg   $dst, $dst\t#@cmpL3_reg_reg"
  %}
  ins_encode %{
    __ cmp_l2i(t0, as_Register($op1$$reg), as_Register($op2$$reg));
    __ mv(as_Register($dst$$reg), t0);
  %}

  ins_pipe(pipe_class_default);
%}

instruct cmpLTMask_reg_reg(iRegINoSp dst, iRegI p, iRegI q)
%{
  match(Set dst (CmpLTMask p q));

  ins_cost(2 * ALU_COST);

  format %{ "slt $dst, $p, $q\t#@cmpLTMask_reg_reg\n\t"
            "subw $dst, zr, $dst\t#@cmpLTMask_reg_reg"
  %}

  ins_encode %{
    __ slt(as_Register($dst$$reg), as_Register($p$$reg), as_Register($q$$reg));
    __ subw(as_Register($dst$$reg), zr, as_Register($dst$$reg));
  %}

  ins_pipe(ialu_reg_reg);
%}

instruct cmpLTMask_reg_zero(iRegINoSp dst, iRegIorL2I op, immI0 zero)
%{
  match(Set dst (CmpLTMask op zero));

  ins_cost(ALU_COST);

  format %{ "sraiw $dst, $dst, 31\t#@cmpLTMask_reg_reg" %}

  ins_encode %{
    __ sraiw(as_Register($dst$$reg), as_Register($op$$reg), 31);
  %}

  ins_pipe(ialu_reg_shift);
%}


// ============================================================================
// Max and Min

instruct minI_rReg(iRegINoSp dst, iRegI src1, iRegI src2)
%{
  match(Set dst (MinI src1 src2));

  effect(DEF dst, USE src1, USE src2);

  ins_cost(BRANCH_COST + ALU_COST * 2);
  format %{
    "ble $src1, $src2, Lsrc1.\t#@minI_rReg\n\t"
    "mv $dst, $src2\n\t"
    "j Ldone\n\t"
    "bind Lsrc1\n\t"
    "mv $dst, $src1\n\t"
    "bind\t#@minI_rReg"
  %}

  ins_encode %{
    Label Lsrc1, Ldone;
    __ ble(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1);
    __ mv(as_Register($dst$$reg), as_Register($src2$$reg));
    __ j(Ldone);
    __ bind(Lsrc1);
    __ mv(as_Register($dst$$reg), as_Register($src1$$reg));
    __ bind(Ldone);
  %}

  ins_pipe(ialu_reg_reg);
%}

instruct maxI_rReg(iRegINoSp dst, iRegI src1, iRegI src2)
%{
  match(Set dst (MaxI src1 src2));

  effect(DEF dst, USE src1, USE src2);

  ins_cost(BRANCH_COST + ALU_COST * 2);
  format %{
    "bge $src1, $src2, Lsrc1\t#@maxI_rReg\n\t"
    "mv $dst, $src2\n\t"
    "j Ldone\n\t"
    "bind Lsrc1\n\t"
    "mv $dst, $src1\n\t"
    "bind\t#@maxI_rReg"
  %}

  ins_encode %{
    Label Lsrc1, Ldone;
    __ bge(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1);
    __ mv(as_Register($dst$$reg), as_Register($src2$$reg));
    __ j(Ldone);
    __ bind(Lsrc1);
    __ mv(as_Register($dst$$reg), as_Register($src1$$reg));
    __ bind(Ldone);

  %}

  ins_pipe(ialu_reg_reg);
%}

// ============================================================================
// Branch Instructions
// Direct Branch.
instruct branch(label lbl)
%{
  match(Goto);

  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "j  $lbl\t#@branch" %}

  ins_encode(riscv_enc_j(lbl));

  ins_pipe(pipe_branch);
%}

// ============================================================================
// Compare and Branch Instructions

// Patterns for short (< 12KiB) variants

// Compare flags and branch near instructions.
instruct cmpFlag_branch(cmpOpEqNe cmp, rFlagsReg cr, label lbl) %{
  match(If cmp cr);
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp  $cr, zr, $lbl\t#@cmpFlag_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($cr$$reg), *($lbl$$label));
  %}
  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare signed int and branch near instructions
instruct cmpI_branch(cmpOp cmp, iRegI op1, iRegI op2, label lbl)
%{
  // Same match rule as `far_cmpI_branch'.
  match(If cmp (CmpI op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpI_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpI_loop(cmpOp cmp, iRegI op1, iRegI op2, label lbl)
%{
  // Same match rule as `far_cmpI_loop'.
  match(CountedLoopEnd cmp (CmpI op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpI_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare unsigned int and branch near instructions
instruct cmpU_branch(cmpOpU cmp, iRegI op1, iRegI op2, label lbl)
%{
  // Same match rule as `far_cmpU_branch'.
  match(If cmp (CmpU op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpU_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpU_loop(cmpOpU cmp, iRegI op1, iRegI op2, label lbl)
%{
  // Same match rule as `far_cmpU_loop'.
  match(CountedLoopEnd cmp (CmpU op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpU_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare signed long and branch near instructions
instruct cmpL_branch(cmpOp cmp, iRegL op1, iRegL op2, label lbl)
%{
  // Same match rule as `far_cmpL_branch'.
  match(If cmp (CmpL op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpL_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpL_loop(cmpOp cmp, iRegL op1, iRegL op2, label lbl)
%{
  // Same match rule as `far_cmpL_loop'.
  match(CountedLoopEnd cmp (CmpL op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpL_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare unsigned long and branch near instructions
instruct cmpUL_branch(cmpOpU cmp, iRegL op1, iRegL op2, label lbl)
%{
  // Same match rule as `far_cmpUL_branch'.
  match(If cmp (CmpUL op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpUL_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpUL_loop(cmpOpU cmp, iRegL op1, iRegL op2, label lbl)
%{
  // Same match rule as `far_cmpUL_loop'.
  match(CountedLoopEnd cmp (CmpUL op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpUL_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare pointer and branch near instructions
instruct cmpP_branch(cmpOpU cmp, iRegP op1, iRegP op2, label lbl)
%{
  // Same match rule as `far_cmpP_branch'.
  match(If cmp (CmpP op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpP_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpP_loop(cmpOpU cmp, iRegP op1, iRegP op2, label lbl)
%{
  // Same match rule as `far_cmpP_loop'.
  match(CountedLoopEnd cmp (CmpP op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpP_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare narrow pointer and branch near instructions
instruct cmpN_branch(cmpOpU cmp, iRegN op1, iRegN op2, label lbl)
%{
  // Same match rule as `far_cmpN_branch'.
  match(If cmp (CmpN op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpN_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

instruct cmpN_loop(cmpOpU cmp, iRegN op1, iRegN op2, label lbl)
%{
  // Same match rule as `far_cmpN_loop'.
  match(CountedLoopEnd cmp (CmpN op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, $op2, $lbl\t#@cmpN_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmp_branch);
  ins_short_branch(1);
%}

// Compare float and branch near instructions
instruct cmpF_branch(cmpOp cmp, fRegF op1, fRegF op2, label lbl)
%{
  // Same match rule as `far_cmpF_branch'.
  match(If cmp (CmpF op1 op2));

  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST);
  format %{ "float_b$cmp $op1, $op2, $lbl \t#@cmpF_branch"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_class_compare);
  ins_short_branch(1);
%}

instruct cmpF_loop(cmpOp cmp, fRegF op1, fRegF op2, label lbl)
%{
  // Same match rule as `far_cmpF_loop'.
  match(CountedLoopEnd cmp (CmpF op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST);
  format %{ "float_b$cmp $op1, $op2, $lbl\t#@cmpF_loop"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_class_compare);
  ins_short_branch(1);
%}

// Compare double and branch near instructions
instruct cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl)
%{
  // Same match rule as `far_cmpD_branch'.
  match(If cmp (CmpD op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST);
  format %{ "double_b$cmp $op1, $op2, $lbl\t#@cmpD_branch"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg),
                        as_FloatRegister($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_class_compare);
  ins_short_branch(1);
%}

instruct cmpD_loop(cmpOp cmp, fRegD op1, fRegD op2, label lbl)
%{
  // Same match rule as `far_cmpD_loop'.
  match(CountedLoopEnd cmp (CmpD op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST);
  format %{ "double_b$cmp $op1, $op2, $lbl\t#@cmpD_loop"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg),
                        as_FloatRegister($op2$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_class_compare);
  ins_short_branch(1);
%}

// Compare signed int with zero and branch near instructions
instruct cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl)
%{
  // Same match rule as `far_cmpI_reg_imm0_branch'.
  match(If cmp (CmpI op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpI_reg_imm0_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl)
%{
  // Same match rule as `far_cmpI_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpI op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpI_reg_imm0_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare unsigned int with zero and branch near instructions
instruct cmpUEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl)
%{
  // Same match rule as `far_cmpUEqNeLeGt_reg_imm0_branch'.
  match(If cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpUEqNeLeGt_reg_imm0_branch" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpUEqNeLeGt_reg_imm0_loop(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl)
%{
  // Same match rule as `far_cmpUEqNeLeGt_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpUEqNeLeGt_reg_imm0_loop" %}


  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare signed long with zero and branch near instructions
instruct cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl)
%{
  // Same match rule as `far_cmpL_reg_imm0_branch'.
  match(If cmp (CmpL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpL_reg_imm0_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl)
%{
  // Same match rule as `far_cmpL_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpL_reg_imm0_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare unsigned long with zero and branch near instructions
instruct cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl)
%{
  // Same match rule as `far_cmpULEqNeLeGt_reg_imm0_branch'.
  match(If cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpULEqNeLeGt_reg_imm0_branch" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpULEqNeLeGt_reg_imm0_loop(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl)
%{
  // Same match rule as `far_cmpULEqNeLeGt_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpULEqNeLeGt_reg_imm0_loop" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare pointer with zero and branch near instructions
instruct cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{
  // Same match rule as `far_cmpP_reg_imm0_branch'.
  match(If cmp (CmpP op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp   $op1, zr, $lbl\t#@cmpP_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpP_imm0_loop(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{
  // Same match rule as `far_cmpP_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpP op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp   $op1, zr, $lbl\t#@cmpP_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare narrow pointer with zero and branch near instructions
instruct cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{
  // Same match rule as `far_cmpN_reg_imm0_branch'.
  match(If cmp (CmpN op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpN_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpN_imm0_loop(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{
  // Same match rule as `far_cmpN_reg_imm0_loop'.
  match(CountedLoopEnd cmp (CmpN op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "b$cmp  $op1, zr, $lbl\t#@cmpN_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Compare narrow pointer with pointer zero and branch near instructions
instruct cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{
  // Same match rule as `far_cmpP_narrowOop_imm0_branch'.
  match(If cmp (CmpP (DecodeN op1) zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp   $op1, zr, $lbl\t#@cmpP_narrowOop_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

instruct cmpP_narrowOop_imm0_loop(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{
  // Same match rule as `far_cmpP_narrowOop_imm0_loop'.
  match(CountedLoopEnd cmp (CmpP (DecodeN op1) zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "b$cmp   $op1, zr, $lbl\t#@cmpP_narrowOop_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label));
  %}

  ins_pipe(pipe_cmpz_branch);
  ins_short_branch(1);
%}

// Patterns for far (20KiB) variants

instruct far_cmpFlag_branch(cmpOp cmp, rFlagsReg cr, label lbl) %{
  match(If cmp cr);
  effect(USE lbl);

  ins_cost(BRANCH_COST);
  format %{ "far_b$cmp $cr, zr, $lbl\t#@far_cmpFlag_branch"%}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($cr$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

// Compare signed int and branch far instructions
instruct far_cmpI_branch(cmpOp cmp, iRegI op1, iRegI op2, label lbl) %{
  match(If cmp (CmpI op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  // the format instruction [far_b$cmp] here is be used as two insructions
  // in macroassembler: b$not_cmp(op1, op2, done), j($lbl), bind(done)
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpI_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpI_loop(cmpOp cmp, iRegI op1, iRegI op2, label lbl) %{
  match(CountedLoopEnd cmp (CmpI op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpI_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpU_branch(cmpOpU cmp, iRegI op1, iRegI op2, label lbl) %{
  match(If cmp (CmpU op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpU_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpU_loop(cmpOpU cmp, iRegI op1, iRegI op2, label lbl) %{
  match(CountedLoopEnd cmp (CmpU op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpU_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpL_branch(cmpOp cmp, iRegL op1, iRegL op2, label lbl) %{
  match(If cmp (CmpL op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpL_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpLloop(cmpOp cmp, iRegL op1, iRegL op2, label lbl) %{
  match(CountedLoopEnd cmp (CmpL op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpL_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpUL_branch(cmpOpU cmp, iRegL op1, iRegL op2, label lbl) %{
  match(If cmp (CmpUL op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpUL_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpUL_loop(cmpOpU cmp, iRegL op1, iRegL op2, label lbl) %{
  match(CountedLoopEnd cmp (CmpUL op1 op2));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpUL_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpP_branch(cmpOpU cmp, iRegP op1, iRegP op2, label lbl)
%{
  match(If cmp (CmpP op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpP_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpP_loop(cmpOpU cmp, iRegP op1, iRegP op2, label lbl)
%{
  match(CountedLoopEnd cmp (CmpP op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpP_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpN_branch(cmpOpU cmp, iRegN op1, iRegN op2, label lbl)
%{
  match(If cmp (CmpN op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpN_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                       as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

instruct far_cmpN_loop(cmpOpU cmp, iRegN op1, iRegN op2, label lbl)
%{
  match(CountedLoopEnd cmp (CmpN op1 op2));

  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, $op2, $lbl\t#@far_cmpN_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg),
                  as_Register($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmp_branch);
%}

// Float compare and branch instructions
instruct far_cmpF_branch(cmpOp cmp, fRegF op1, fRegF op2, label lbl)
%{
  match(If cmp (CmpF op1 op2));

  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST * 2);
  format %{ "far_float_b$cmp $op1, $op2, $lbl\t#@far_cmpF_branch"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg),
                        *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_class_compare);
%}

instruct far_cmpF_loop(cmpOp cmp, fRegF op1, fRegF op2, label lbl)
%{
  match(CountedLoopEnd cmp (CmpF op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST * 2);
  format %{ "far_float_b$cmp $op1, $op2, $lbl\t#@far_cmpF_loop"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg),
                        *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_class_compare);
%}

// Double compare and branch instructions
instruct far_cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl)
%{
  match(If cmp (CmpD op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST * 2);
  format %{ "far_double_b$cmp $op1, $op2, $lbl\t#@far_cmpD_branch"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg),
                        as_FloatRegister($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_class_compare);
%}

instruct far_cmpD_loop(cmpOp cmp, fRegD op1, fRegD op2, label lbl)
%{
  match(CountedLoopEnd cmp (CmpD op1 op2));
  effect(USE lbl);

  ins_cost(XFER_COST + BRANCH_COST * 2);
  format %{ "far_double_b$cmp $op1, $op2, $lbl\t#@far_cmpD_loop"%}

  ins_encode %{
    __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg),
                        as_FloatRegister($op2$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_class_compare);
%}

instruct far_cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(If cmp (CmpI op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpI_reg_imm0_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpI op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpI_reg_imm0_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpUEqNeLeGt_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(If cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpUEqNeLeGt_imm0_branch" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpUEqNeLeGt_reg_imm0_loop(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpUEqNeLeGt_reg_imm0_loop" %}


  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

// compare lt/ge unsigned instructs has no short instruct with same match
instruct far_cmpULtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(If cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "j  $lbl if $cmp == ge\t#@far_cmpULtGe_reg_imm0_branch" %}

  ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl));

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpULtGe_reg_imm0_loop(cmpOpULtGe cmp, iRegI op1, immI0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpU op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "j  $lbl if $cmp == ge\t#@far_cmpULtGe_reg_imm0_loop" %}

  ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl));

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(If cmp (CmpL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpL_reg_imm0_branch" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpL_reg_imm0_loop" %}

  ins_encode %{
    __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(If cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpULEqNeLeGt_reg_imm0_branch" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpULEqNeLeGt_reg_imm0_loop(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpULEqNeLeGt_reg_imm0_loop" %}

  ins_encode %{
    __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

// compare lt/ge unsigned instructs has no short instruct with same match
instruct far_cmpULLtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(If cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "j  $lbl if $cmp == ge\t#@far_cmpULLtGe_reg_imm0_branch" %}

  ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl));

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpULLtGe_reg_imm0_loop(cmpOpULtGe cmp, iRegL op1, immL0 zero, label lbl)
%{
  match(CountedLoopEnd cmp (CmpUL op1 zero));

  effect(USE op1, USE lbl);

  ins_cost(BRANCH_COST);

  format %{ "j  $lbl if $cmp == ge\t#@far_cmpULLtGe_reg_imm0_loop" %}

  ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl));

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{
  match(If cmp (CmpP op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp   $op1, zr, $lbl\t#@far_cmpP_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpP_imm0_loop(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{
  match(CountedLoopEnd cmp (CmpP op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp   $op1, zr, $lbl\t#@far_cmpP_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{
  match(If cmp (CmpN op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpN_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpN_imm0_loop(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{
  match(CountedLoopEnd cmp (CmpN op1 zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);

  format %{ "far_b$cmp  $op1, zr, $lbl\t#@far_cmpN_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{
  match(If cmp (CmpP (DecodeN op1) zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp   $op1, zr, $lbl\t#@far_cmpP_narrowOop_imm0_branch" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

instruct far_cmpP_narrowOop_imm0_loop(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{
  match(CountedLoopEnd cmp (CmpP (DecodeN op1) zero));
  effect(USE lbl);

  ins_cost(BRANCH_COST * 2);
  format %{ "far_b$cmp   $op1, zr, $lbl\t#@far_cmpP_narrowOop_imm0_loop" %}

  ins_encode %{
    __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true);
  %}

  ins_pipe(pipe_cmpz_branch);
%}

// ============================================================================
// Conditional Move Instructions
instruct cmovI_cmpI(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOp cop) %{
  match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);

  format %{
             "bneg$cop $op1, $op2, skip\t#@cmovI_cmpI\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}

instruct cmovI_cmpU(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOpU cop) %{
  match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);

  format %{
             "bneg$cop $op1, $op2, skip\t#@cmovI_cmpU\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}

instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) %{
  match(Set dst (CMoveI (Binary cop (CmpL op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);

  format %{
             "bneg$cop $op1, $op2, skip\t#@cmovI_cmpL\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}

instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) %{
  match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);

  format %{
             "bneg$cop $op1, $op2, skip\t#@cmovL_cmpL\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}

instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) %{
  match(Set dst (CMoveL (Binary cop (CmpUL op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);

  format %{
             "bneg$cop $op1, $op2, skip\t#@cmovL_cmpUL\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}

instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) %{
  match(Set dst (CMoveI (Binary cop (CmpUL op1 op2)) (Binary dst src)));
  ins_cost(ALU_COST + BRANCH_COST);
  format %{
             "bneg$cop $op1, $op2\t#@cmovI_cmpUL\n\t"
             "mv $dst, $src\n\t"
             "skip:"
         %}

  ins_encode %{
    __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask,
                 as_Register($op1$$reg), as_Register($op2$$reg),
                 as_Register($dst$$reg), as_Register($src$$reg));
  %}

  ins_pipe(pipe_slow);
%}


// ============================================================================
// Procedure Call/Return Instructions

// Call Java Static Instruction
// Note: If this code changes, the corresponding ret_addr_offset() and
//       compute_padding() functions will have to be adjusted.
instruct CallStaticJavaDirect(method meth)
%{
  match(CallStaticJava);

  effect(USE meth);

  ins_cost(BRANCH_COST);

  format %{ "CALL,static $meth\t#@CallStaticJavaDirect" %}

  ins_encode(riscv_enc_java_static_call(meth),
             riscv_enc_call_epilog);

  ins_pipe(pipe_class_call);
  ins_alignment(4);
%}

// TO HERE

// Call Java Dynamic Instruction
// Note: If this code changes, the corresponding ret_addr_offset() and
//       compute_padding() functions will have to be adjusted.
instruct CallDynamicJavaDirect(method meth, rFlagsReg cr)
%{
  match(CallDynamicJava);

  effect(USE meth, KILL cr);

  ins_cost(BRANCH_COST + ALU_COST * 6);

  format %{ "CALL,dynamic $meth\t#@CallDynamicJavaDirect" %}

  ins_encode(riscv_enc_java_dynamic_call(meth),
             riscv_enc_call_epilog);

  ins_pipe(pipe_class_call);
  ins_alignment(4);
%}

// Call Runtime Instruction

instruct CallRuntimeDirect(method meth, rFlagsReg cr)
%{
  match(CallRuntime);

  effect(USE meth, KILL cr);

  ins_cost(BRANCH_COST);

  format %{ "CALL, runtime $meth\t#@CallRuntimeDirect" %}

  ins_encode(riscv_enc_java_to_runtime(meth));

  ins_pipe(pipe_class_call);
%}

// Call Runtime Instruction

instruct CallLeafDirect(method meth, rFlagsReg cr)
%{
  match(CallLeaf);

  effect(USE meth, KILL cr);

  ins_cost(BRANCH_COST);

  format %{ "CALL, runtime leaf $meth\t#@CallLeafDirect" %}

  ins_encode(riscv_enc_java_to_runtime(meth));

  ins_pipe(pipe_class_call);
%}

// Call Runtime Instruction

instruct CallLeafNoFPDirect(method meth, rFlagsReg cr)
%{
  match(CallLeafNoFP);

  effect(USE meth, KILL cr);

  ins_cost(BRANCH_COST);

  format %{ "CALL, runtime leaf nofp $meth\t#@CallLeafNoFPDirect" %}

  ins_encode(riscv_enc_java_to_runtime(meth));

  ins_pipe(pipe_class_call);
%}

// ============================================================================
// Partial Subtype Check
//
// superklass array for an instance of the superklass.  Set a hidden
// internal cache on a hit (cache is checked with exposed code in
// gen_subtype_check()).  Return zero for a hit.  The encoding
// ALSO sets flags.

instruct partialSubtypeCheck(iRegP_R15 result, iRegP_R14 sub, iRegP_R10 super, iRegP_R12 tmp, rFlagsReg cr)
%{
  match(Set result (PartialSubtypeCheck sub super));
  effect(KILL tmp, KILL cr);

  ins_cost(2 * STORE_COST + 3 * LOAD_COST + 4 * ALU_COST + BRANCH_COST * 4);
  format %{ "partialSubtypeCheck $result, $sub, $super\t#@partialSubtypeCheck" %}

  ins_encode(riscv_enc_partial_subtype_check(sub, super, tmp, result));

  opcode(0x1); // Force zero of result reg on hit

  ins_pipe(pipe_class_memory);
%}

instruct partialSubtypeCheckVsZero(iRegP_R15 result, iRegP_R14 sub, iRegP_R10 super, iRegP_R12 tmp,
                                   immP0 zero, rFlagsReg cr)
%{
  match(Set cr (CmpP (PartialSubtypeCheck sub super) zero));
  effect(KILL tmp, KILL result);

  ins_cost(2 * STORE_COST + 3 * LOAD_COST + 4 * ALU_COST + BRANCH_COST * 4);
  format %{ "partialSubtypeCheck $result, $sub, $super == 0\t#@partialSubtypeCheckVsZero" %}

  ins_encode(riscv_enc_partial_subtype_check(sub, super, tmp, result));

  opcode(0x0); // Don't zero result reg on hit

  ins_pipe(pipe_class_memory);
%}

instruct string_compareU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                         iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::UU);
  match(Set result (StrComp(Binary str1 cnt1)(Binary str2 cnt2)));
  effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);

  format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareU" %}
  ins_encode %{
    // Count is in 8-bit bytes; non-Compact chars are 16 bits.
    __ string_compare($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register, $result$$Register,
                      $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                      StrIntrinsicNode::UU);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_compareL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                         iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::LL);
  match(Set result (StrComp(Binary str1 cnt1)(Binary str2 cnt2)));
  effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);

  format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareL" %}
  ins_encode %{
    __ string_compare($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register, $result$$Register,
                      $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                      StrIntrinsicNode::LL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_compareUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                          iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::UL);
  match(Set result (StrComp(Binary str1 cnt1)(Binary str2 cnt2)));
  effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);

  format %{"String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareUL" %}
  ins_encode %{
    __ string_compare($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register, $result$$Register,
                      $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                      StrIntrinsicNode::UL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_compareLU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                          iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3,
                          rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::LU);
  match(Set result (StrComp(Binary str1 cnt1)(Binary str2 cnt2)));
  effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr);

  format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareLU" %}
  ins_encode %{
    __ string_compare($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register, $result$$Register,
                      $tmp1$$Register, $tmp2$$Register, $tmp3$$Register,
                      StrIntrinsicNode::LU);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexofUU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                          iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3,
                          iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr);

  format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UU)" %}
  ins_encode %{
    __ string_indexof($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register,
                      $tmp1$$Register, $tmp2$$Register,
                      $tmp3$$Register, $tmp4$$Register,
                      $tmp5$$Register, $tmp6$$Register,
                      $result$$Register, StrIntrinsicNode::UU);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexofLL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                          iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3,
                          iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr);

  format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (LL)" %}
  ins_encode %{
    __ string_indexof($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register,
                      $tmp1$$Register, $tmp2$$Register,
                      $tmp3$$Register, $tmp4$$Register,
                      $tmp5$$Register, $tmp6$$Register,
                      $result$$Register, StrIntrinsicNode::LL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexofUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2,
                          iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3,
                          iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr);
  format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UL)" %}

  ins_encode %{
    __ string_indexof($str1$$Register, $str2$$Register,
                      $cnt1$$Register, $cnt2$$Register,
                      $tmp1$$Register, $tmp2$$Register,
                      $tmp3$$Register, $tmp4$$Register,
                      $tmp5$$Register, $tmp6$$Register,
                      $result$$Register, StrIntrinsicNode::UL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexof_conUU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2,
                              immI_le_4 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2,
                              iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr);

  format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UU)" %}

  ins_encode %{
    int icnt2 = (int)$int_cnt2$$constant;
    __ string_indexof_linearscan($str1$$Register, $str2$$Register,
                                 $cnt1$$Register, zr,
                                 $tmp1$$Register, $tmp2$$Register,
                                 $tmp3$$Register, $tmp4$$Register,
                                 icnt2, $result$$Register, StrIntrinsicNode::UU);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexof_conLL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2,
                              immI_le_4 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2,
                              iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr);

  format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (LL)" %}
  ins_encode %{
    int icnt2 = (int)$int_cnt2$$constant;
    __ string_indexof_linearscan($str1$$Register, $str2$$Register,
                                 $cnt1$$Register, zr,
                                 $tmp1$$Register, $tmp2$$Register,
                                 $tmp3$$Register, $tmp4$$Register,
                                 icnt2, $result$$Register, StrIntrinsicNode::LL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_indexof_conUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2,
                              immI_1 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2,
                              iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr)
%{
  predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL);
  match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr);

  format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UL)" %}
  ins_encode %{
    int icnt2 = (int)$int_cnt2$$constant;
    __ string_indexof_linearscan($str1$$Register, $str2$$Register,
                                 $cnt1$$Register, zr,
                                 $tmp1$$Register, $tmp2$$Register,
                                 $tmp3$$Register, $tmp4$$Register,
                                 icnt2, $result$$Register, StrIntrinsicNode::UL);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct stringU_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch,
                              iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2,
                              iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr)
%{
  match(Set result (StrIndexOfChar (Binary str1 cnt1) ch));
  predicate(!UseRVV && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U));
  effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr);

  format %{ "StringUTF16 IndexOf char[] $str1,$cnt1,$ch -> $result" %}
  ins_encode %{
    __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register,
                           $result$$Register, $tmp1$$Register, $tmp2$$Register,
                           $tmp3$$Register, $tmp4$$Register, false /* isU */);
  %}
  ins_pipe(pipe_class_memory);
%}


instruct stringL_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch,
                              iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2,
                              iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr)
%{
  match(Set result (StrIndexOfChar (Binary str1 cnt1) ch));
  predicate(!UseRVV && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L));
  effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP_DEF result,
         TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr);

  format %{ "StringUTF16 IndexOf char[] $str1,$cnt1,$ch -> $result" %}
  ins_encode %{
    __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register,
                           $result$$Register, $tmp1$$Register, $tmp2$$Register,
                           $tmp3$$Register, $tmp4$$Register, true /* isL */);
  %}
  ins_pipe(pipe_class_memory);
%}

// clearing of an array
instruct clearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, iRegP_R30 tmp1,
                            iRegP_R31 tmp2, Universe dummy)
%{
  // temp registers must match the one used in StubGenerator::generate_zero_blocks()
  predicate(UseBlockZeroing || !UseRVV);
  match(Set dummy (ClearArray cnt base));
  effect(USE_KILL cnt, USE_KILL base, TEMP tmp1, TEMP tmp2);

  ins_cost(4 * DEFAULT_COST);
  format %{ "ClearArray $cnt, $base\t#@clearArray_reg_reg" %}

  ins_encode %{
    address tpc = __ zero_words($base$$Register, $cnt$$Register);
    if (tpc == NULL) {
      ciEnv::current()->record_failure("CodeCache is full");
      return;
    }
  %}

  ins_pipe(pipe_class_memory);
%}

instruct clearArray_imm_reg(immL cnt, iRegP_R28 base, Universe dummy, rFlagsReg cr)
%{
  predicate(!UseRVV && (uint64_t)n->in(2)->get_long()
            < (uint64_t)(BlockZeroingLowLimit >> LogBytesPerWord));
  match(Set dummy (ClearArray cnt base));
  effect(USE_KILL base, KILL cr);

  ins_cost(4 * DEFAULT_COST);
  format %{ "ClearArray $cnt, $base\t#@clearArray_imm_reg" %}

  ins_encode %{
    __ zero_words($base$$Register, (uint64_t)$cnt$$constant);
  %}

  ins_pipe(pipe_class_memory);
%}

instruct string_equalsL(iRegP_R11 str1, iRegP_R13 str2, iRegI_R14 cnt,
                        iRegI_R10 result, rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL);
  match(Set result (StrEquals (Binary str1 str2) cnt));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr);

  format %{ "String Equals $str1, $str2, $cnt -> $result\t#@string_equalsL" %}
  ins_encode %{
    // Count is in 8-bit bytes; non-Compact chars are 16 bits.
    __ string_equals($str1$$Register, $str2$$Register,
                     $result$$Register, $cnt$$Register, 1);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct string_equalsU(iRegP_R11 str1, iRegP_R13 str2, iRegI_R14 cnt,
                        iRegI_R10 result, rFlagsReg cr)
%{
  predicate(!UseRVV && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::UU);
  match(Set result (StrEquals (Binary str1 str2) cnt));
  effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr);

  format %{ "String Equals $str1, $str2, $cnt -> $result\t#@string_equalsU" %}
  ins_encode %{
    // Count is in 8-bit bytes; non-Compact chars are 16 bits.
    __ string_equals($str1$$Register, $str2$$Register,
                     $result$$Register, $cnt$$Register, 2);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct array_equalsB(iRegP_R11 ary1, iRegP_R12 ary2, iRegI_R10 result,
                       iRegP_R13 tmp1, iRegP_R14 tmp2, iRegP_R15 tmp3,
                       iRegP_R16 tmp4, iRegP_R28 tmp5, rFlagsReg cr)
%{
  predicate(!UseRVV && ((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL);
  match(Set result (AryEq ary1 ary2));
  effect(USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL tmp5, KILL cr);

  format %{ "Array Equals $ary1, ary2 -> $result\t#@array_equalsB // KILL $tmp5" %}
  ins_encode %{
    __ arrays_equals($ary1$$Register, $ary2$$Register,
                     $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register,
                     $result$$Register, $tmp5$$Register, 1);
  %}
  ins_pipe(pipe_class_memory);
%}

instruct array_equalsC(iRegP_R11 ary1, iRegP_R12 ary2, iRegI_R10 result,
                       iRegP_R13 tmp1, iRegP_R14 tmp2, iRegP_R15 tmp3,
                       iRegP_R16 tmp4, iRegP_R28 tmp5, rFlagsReg cr)
%{
  predicate(!UseRVV && ((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU);
  match(Set result (AryEq ary1 ary2));
  effect(USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL tmp5, KILL cr);

  format %{ "Array Equals $ary1, ary2 -> $result\t#@array_equalsC // KILL $tmp5" %}
  ins_encode %{
    __ arrays_equals($ary1$$Register, $ary2$$Register,
                     $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register,
                     $result$$Register, $tmp5$$Register, 2);
  %}
  ins_pipe(pipe_class_memory);
%}

// ============================================================================
// Safepoint Instructions

instruct safePoint(iRegP poll)
%{
  match(SafePoint poll);

  ins_cost(2 * LOAD_COST);
  format %{
    "lwu zr, [$poll]\t# Safepoint: poll for GC, #@safePoint"
  %}
  ins_encode %{
    __ read_polling_page(as_Register($poll$$reg), 0, relocInfo::poll_type);
  %}
  ins_pipe(pipe_serial); // ins_pipe(iload_reg_mem);
%}

// ============================================================================
// This name is KNOWN by the ADLC and cannot be changed.
// The ADLC forces a 'TypeRawPtr::BOTTOM' output type
// for this guy.
instruct tlsLoadP(javaThread_RegP dst)
%{
  match(Set dst (ThreadLocal));

  ins_cost(0);

  format %{ " -- \t// $dst=Thread::current(), empty, #@tlsLoadP" %}

  size(0);

  ins_encode( /*empty*/ );

  ins_pipe(pipe_class_empty);
%}

// inlined locking and unlocking
// using t1 as the 'flag' register to bridge the BoolNode producers and consumers
instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2)
%{
  match(Set cr (FastLock object box));
  effect(TEMP tmp1, TEMP tmp2);

  ins_cost(LOAD_COST * 2 + STORE_COST * 3 + ALU_COST * 6 + BRANCH_COST * 3);
  format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2, #@cmpFastLock" %}

  ins_encode(riscv_enc_fast_lock(object, box, tmp1, tmp2));

  ins_pipe(pipe_serial);
%}

// using t1 as the 'flag' register to bridge the BoolNode producers and consumers
instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2)
%{
  match(Set cr (FastUnlock object box));
  effect(TEMP tmp1, TEMP tmp2);

  ins_cost(LOAD_COST * 2 + STORE_COST + ALU_COST * 2 + BRANCH_COST * 4);
  format %{ "fastunlock $object,$box\t! kills $tmp1, $tmp2, #@cmpFastUnlock" %}

  ins_encode(riscv_enc_fast_unlock(object, box, tmp1, tmp2));

  ins_pipe(pipe_serial);
%}

// Tail Call; Jump from runtime stub to Java code.
// Also known as an 'interprocedural jump'.
// Target of jump will eventually return to caller.
// TailJump below removes the return address.
instruct TailCalljmpInd(iRegPNoSp jump_target, inline_cache_RegP method_oop)
%{
  match(TailCall jump_target method_oop);

  ins_cost(BRANCH_COST);

  format %{ "jalr $jump_target\t# $method_oop holds method oop, #@TailCalljmpInd." %}

  ins_encode(riscv_enc_tail_call(jump_target));

  ins_pipe(pipe_class_call);
%}

instruct TailjmpInd(iRegPNoSp jump_target, iRegP_R10 ex_oop)
%{
  match(TailJump jump_target ex_oop);

  ins_cost(ALU_COST + BRANCH_COST);

  format %{ "jalr $jump_target\t# $ex_oop holds exception oop, #@TailjmpInd." %}

  ins_encode(riscv_enc_tail_jmp(jump_target));

  ins_pipe(pipe_class_call);
%}

// Create exception oop: created by stack-crawling runtime code.
// Created exception is now available to this handler, and is setup
// just prior to jumping to this handler. No code emitted.
instruct CreateException(iRegP_R10 ex_oop)
%{
  match(Set ex_oop (CreateEx));

  ins_cost(0);
  format %{ " -- \t// exception oop; no code emitted, #@CreateException" %}

  size(0);

  ins_encode( /*empty*/ );

  ins_pipe(pipe_class_empty);
%}

// Rethrow exception: The exception oop will come in the first
// argument position. Then JUMP (not call) to the rethrow stub code.
instruct RethrowException()
%{
  match(Rethrow);

  ins_cost(BRANCH_COST);

  format %{ "j rethrow_stub\t#@RethrowException" %}

  ins_encode(riscv_enc_rethrow());

  ins_pipe(pipe_class_call);
%}

// Return Instruction
// epilog node loads ret address into ra as part of frame pop
instruct Ret()
%{
  match(Return);

  ins_cost(BRANCH_COST);
  format %{ "ret\t// return register, #@Ret" %}

  ins_encode(riscv_enc_ret());

  ins_pipe(pipe_branch);
%}

// Die now.
instruct ShouldNotReachHere() %{
  match(Halt);

  ins_cost(BRANCH_COST);

  format %{ "#@ShouldNotReachHere" %}

  ins_encode %{
    if (is_reachable()) {
      __ stop(_halt_reason);
    }
  %}

  ins_pipe(pipe_class_default);
%}


//----------PEEPHOLE RULES-----------------------------------------------------
// These must follow all instruction definitions as they use the names
// defined in the instructions definitions.
//
// peepmatch ( root_instr_name [preceding_instruction]* );
//
// peepconstraint %{
// (instruction_number.operand_name relational_op instruction_number.operand_name
//  [, ...] );
// // instruction numbers are zero-based using left to right order in peepmatch
//
// peepreplace ( instr_name  ( [instruction_number.operand_name]* ) );
// // provide an instruction_number.operand_name for each operand that appears
// // in the replacement instruction's match rule
//
// ---------VM FLAGS---------------------------------------------------------
//
// All peephole optimizations can be turned off using -XX:-OptoPeephole
//
// Each peephole rule is given an identifying number starting with zero and
// increasing by one in the order seen by the parser.  An individual peephole
// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=#
// on the command-line.
//
// ---------CURRENT LIMITATIONS----------------------------------------------
//
// Only match adjacent instructions in same basic block
// Only equality constraints
// Only constraints between operands, not (0.dest_reg == RAX_enc)
// Only one replacement instruction
//
//----------SMARTSPILL RULES---------------------------------------------------
// These must follow all instruction definitions as they use the names
// defined in the instructions definitions.

// Local Variables:
// mode: c++
// End:

[0.253QuellennavigatorsProjekt 2026-04-26]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge